diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 184 | 
1 files changed, 101 insertions, 83 deletions
| diff --git a/kernel/module.c b/kernel/module.c index a7615d661910..49a405891587 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -307,24 +307,6 @@ int unregister_module_notifier(struct notifier_block *nb)  }  EXPORT_SYMBOL(unregister_module_notifier); -struct load_info { -	const char *name; -	Elf_Ehdr *hdr; -	unsigned long len; -	Elf_Shdr *sechdrs; -	char *secstrings, *strtab; -	unsigned long symoffs, stroffs; -	struct _ddebug *debug; -	unsigned int num_debug; -	bool sig_ok; -#ifdef CONFIG_KALLSYMS -	unsigned long mod_kallsyms_init_off; -#endif -	struct { -		unsigned int sym, str, mod, vers, info, pcpu; -	} index; -}; -  /*   * We require a truly strong try_module_get(): 0 means success.   * Otherwise an error is returned due to ongoing or failed @@ -547,12 +529,30 @@ static bool check_symbol(const struct symsearch *syms,  	return true;  } +static unsigned long kernel_symbol_value(const struct kernel_symbol *sym) +{ +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +	return (unsigned long)offset_to_ptr(&sym->value_offset); +#else +	return sym->value; +#endif +} + +static const char *kernel_symbol_name(const struct kernel_symbol *sym) +{ +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +	return offset_to_ptr(&sym->name_offset); +#else +	return sym->name; +#endif +} +  static int cmp_name(const void *va, const void *vb)  {  	const char *a;  	const struct kernel_symbol *b;  	a = va; b = vb; -	return strcmp(a, b->name); +	return strcmp(a, kernel_symbol_name(b));  }  static bool find_symbol_in_section(const struct symsearch *syms, @@ -1339,14 +1339,12 @@ static inline int check_modstruct_version(const struct load_info *info,  	 * locking is necessary -- use preempt_disable() to placate lockdep.  	 */  	preempt_disable(); -	if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL, -			 &crc, true, false)) { +	if (!find_symbol("module_layout", NULL, &crc, true, false)) {  		preempt_enable();  		BUG();  	}  	preempt_enable(); -	return check_version(info, VMLINUX_SYMBOL_STR(module_layout), -			     mod, crc); +	return check_version(info, "module_layout", mod, crc);  }  /* First part is kernel version, which we ignore if module has crcs. */ @@ -2059,21 +2057,19 @@ static int copy_module_elf(struct module *mod, struct load_info *info)  	/* Elf section header table */  	size = sizeof(*info->sechdrs) * info->hdr->e_shnum; -	mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL); +	mod->klp_info->sechdrs = kmemdup(info->sechdrs, size, GFP_KERNEL);  	if (mod->klp_info->sechdrs == NULL) {  		ret = -ENOMEM;  		goto free_info;  	} -	memcpy(mod->klp_info->sechdrs, info->sechdrs, size);  	/* Elf section name string table */  	size = info->sechdrs[info->hdr->e_shstrndx].sh_size; -	mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL); +	mod->klp_info->secstrings = kmemdup(info->secstrings, size, GFP_KERNEL);  	if (mod->klp_info->secstrings == NULL) {  		ret = -ENOMEM;  		goto free_sechdrs;  	} -	memcpy(mod->klp_info->secstrings, info->secstrings, size);  	/* Elf symbol section index */  	symndx = info->index.sym; @@ -2192,7 +2188,7 @@ void *__symbol_get(const char *symbol)  		sym = NULL;  	preempt_enable(); -	return sym ? (void *)sym->value : NULL; +	return sym ? (void *)kernel_symbol_value(sym) : NULL;  }  EXPORT_SYMBOL_GPL(__symbol_get); @@ -2222,10 +2218,12 @@ static int verify_export_symbols(struct module *mod)  	for (i = 0; i < ARRAY_SIZE(arr); i++) {  		for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { -			if (find_symbol(s->name, &owner, NULL, true, false)) { +			if (find_symbol(kernel_symbol_name(s), &owner, NULL, +					true, false)) {  				pr_err("%s: exports duplicate symbol %s"  				       " (owned by %s)\n", -				       mod->name, s->name, module_name(owner)); +				       mod->name, kernel_symbol_name(s), +				       module_name(owner));  				return -ENOEXEC;  			}  		} @@ -2274,7 +2272,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)  			ksym = resolve_symbol_wait(mod, info, name);  			/* Ok if resolved.  */  			if (ksym && !IS_ERR(ksym)) { -				sym[i].st_value = ksym->value; +				sym[i].st_value = kernel_symbol_value(ksym);  				break;  			} @@ -2282,9 +2280,9 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)  			if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)  				break; -			pr_warn("%s: Unknown symbol %s (err %li)\n", -				mod->name, name, PTR_ERR(ksym));  			ret = PTR_ERR(ksym) ?: -ENOENT; +			pr_warn("%s: Unknown symbol %s (err %d)\n", +				mod->name, name, ret);  			break;  		default: @@ -2486,7 +2484,11 @@ static char *get_modinfo(struct load_info *info, const char *tag)  	Elf_Shdr *infosec = &info->sechdrs[info->index.info];  	unsigned long size = infosec->sh_size; -	for (p = (char *)infosec->sh_addr; p; p = next_string(p, &size)) { +	/* +	 * get_modinfo() calls made before rewrite_section_headers() +	 * must use sh_offset, as sh_addr isn't set! +	 */ +	for (p = (char *)info->hdr + infosec->sh_offset; p; p = next_string(p, &size)) {  		if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')  			return p + taglen + 1;  	} @@ -2534,7 +2536,7 @@ static int is_exported(const char *name, unsigned long value,  		ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);  	else  		ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); -	return ks != NULL && ks->value == value; +	return ks != NULL && kernel_symbol_value(ks) == value;  }  /* As per nm */ @@ -2774,7 +2776,7 @@ static int module_sig_check(struct load_info *info, int flags)  	    memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {  		/* We truncate the module to discard the signature */  		info->len -= markerlen; -		err = mod_verify_sig(mod, &info->len); +		err = mod_verify_sig(mod, info);  	}  	if (!err) { @@ -2926,17 +2928,7 @@ static int rewrite_section_headers(struct load_info *info, int flags)  	}  	/* Track but don't keep modinfo and version sections. */ -	if (flags & MODULE_INIT_IGNORE_MODVERSIONS) -		info->index.vers = 0; /* Pretend no __versions section! */ -	else -		info->index.vers = find_sec(info, "__versions");  	info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC; - -	info->index.info = find_sec(info, ".modinfo"); -	if (!info->index.info) -		info->name = "(missing .modinfo section)"; -	else -		info->name = get_modinfo(info, "name");  	info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;  	return 0; @@ -2947,23 +2939,24 @@ static int rewrite_section_headers(struct load_info *info, int flags)   * search for module section index etc), and do some basic section   * verification.   * - * Return the temporary module pointer (we'll replace it with the final - * one when we move the module sections around). + * Set info->mod to the temporary copy of the module in info->hdr. The final one + * will be allocated in move_module().   */ -static struct module *setup_load_info(struct load_info *info, int flags) +static int setup_load_info(struct load_info *info, int flags)  {  	unsigned int i; -	int err; -	struct module *mod;  	/* Set up the convenience variables */  	info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;  	info->secstrings = (void *)info->hdr  		+ info->sechdrs[info->hdr->e_shstrndx].sh_offset; -	err = rewrite_section_headers(info, flags); -	if (err) -		return ERR_PTR(err); +	/* Try to find a name early so we can log errors with a module name */ +	info->index.info = find_sec(info, ".modinfo"); +	if (!info->index.info) +		info->name = "(missing .modinfo section)"; +	else +		info->name = get_modinfo(info, "name");  	/* Find internal symbols and strings. */  	for (i = 1; i < info->hdr->e_shnum; i++) { @@ -2976,34 +2969,35 @@ static struct module *setup_load_info(struct load_info *info, int flags)  		}  	} +	if (info->index.sym == 0) { +		pr_warn("%s: module has no symbols (stripped?)\n", info->name); +		return -ENOEXEC; +	} +  	info->index.mod = find_sec(info, ".gnu.linkonce.this_module");  	if (!info->index.mod) {  		pr_warn("%s: No module found in object\n",  			info->name ?: "(missing .modinfo name field)"); -		return ERR_PTR(-ENOEXEC); +		return -ENOEXEC;  	}  	/* This is temporary: point mod into copy of data. */ -	mod = (void *)info->sechdrs[info->index.mod].sh_addr; +	info->mod = (void *)info->hdr + info->sechdrs[info->index.mod].sh_offset;  	/* -	 * If we didn't load the .modinfo 'name' field, fall back to +	 * If we didn't load the .modinfo 'name' field earlier, fall back to  	 * on-disk struct mod 'name' field.  	 */  	if (!info->name) -		info->name = mod->name; +		info->name = info->mod->name; -	if (info->index.sym == 0) { -		pr_warn("%s: module has no symbols (stripped?)\n", info->name); -		return ERR_PTR(-ENOEXEC); -	} +	if (flags & MODULE_INIT_IGNORE_MODVERSIONS) +		info->index.vers = 0; /* Pretend no __versions section! */ +	else +		info->index.vers = find_sec(info, "__versions");  	info->index.pcpu = find_pcpusec(info); -	/* Check module struct version now, before we try to use module. */ -	if (!check_modstruct_version(info, mod)) -		return ERR_PTR(-ENOEXEC); - -	return mod; +	return 0;  }  static int check_modinfo(struct module *mod, struct load_info *info, int flags) @@ -3298,25 +3292,17 @@ core_param(module_blacklist, module_blacklist, charp, 0400);  static struct module *layout_and_allocate(struct load_info *info, int flags)  { -	/* Module within temporary copy. */  	struct module *mod;  	unsigned int ndx;  	int err; -	mod = setup_load_info(info, flags); -	if (IS_ERR(mod)) -		return mod; - -	if (blacklisted(info->name)) -		return ERR_PTR(-EPERM); - -	err = check_modinfo(mod, info, flags); +	err = check_modinfo(info->mod, info, flags);  	if (err)  		return ERR_PTR(err);  	/* Allow arches to frob section contents and sizes.  */  	err = module_frob_arch_sections(info->hdr, info->sechdrs, -					info->secstrings, mod); +					info->secstrings, info->mod);  	if (err < 0)  		return ERR_PTR(err); @@ -3331,15 +3317,24 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)  	ndx = find_sec(info, ".data..ro_after_init");  	if (ndx)  		info->sechdrs[ndx].sh_flags |= SHF_RO_AFTER_INIT; +	/* +	 * Mark the __jump_table section as ro_after_init as well: these data +	 * structures are never modified, with the exception of entries that +	 * refer to code in the __init section, which are annotated as such +	 * at module load time. +	 */ +	ndx = find_sec(info, "__jump_table"); +	if (ndx) +		info->sechdrs[ndx].sh_flags |= SHF_RO_AFTER_INIT;  	/* Determine total sizes, and put offsets in sh_entsize.  For now  	   this is done generically; there doesn't appear to be any  	   special cases for the architectures. */ -	layout_sections(mod, info); -	layout_symtab(mod, info); +	layout_sections(info->mod, info); +	layout_symtab(info->mod, info);  	/* Allocate and move to the final place */ -	err = move_module(mod, info); +	err = move_module(info->mod, info);  	if (err)  		return ERR_PTR(err); @@ -3657,17 +3652,36 @@ static int load_module(struct load_info *info, const char __user *uargs,  		       int flags)  {  	struct module *mod; -	long err; +	long err = 0;  	char *after_dashes; +	err = elf_header_check(info); +	if (err) +		goto free_copy; + +	err = setup_load_info(info, flags); +	if (err) +		goto free_copy; + +	if (blacklisted(info->name)) { +		err = -EPERM; +		goto free_copy; +	} +  	err = module_sig_check(info, flags);  	if (err)  		goto free_copy; -	err = elf_header_check(info); +	err = rewrite_section_headers(info, flags);  	if (err)  		goto free_copy; +	/* Check module struct version now, before we try to use module. */ +	if (!check_modstruct_version(info, info->mod)) { +		err = -ENOEXEC; +		goto free_copy; +	} +  	/* Figure out module layout, and allocate all the memory. */  	mod = layout_and_allocate(info, flags);  	if (IS_ERR(mod)) { @@ -4067,7 +4081,7 @@ static unsigned long mod_find_symname(struct module *mod, const char *name)  	for (i = 0; i < kallsyms->num_symtab; i++)  		if (strcmp(name, symname(kallsyms, i)) == 0 && -		    kallsyms->symtab[i].st_info != 'U') +		    kallsyms->symtab[i].st_shndx != SHN_UNDEF)  			return kallsyms->symtab[i].st_value;  	return 0;  } @@ -4113,6 +4127,10 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,  		if (mod->state == MODULE_STATE_UNFORMED)  			continue;  		for (i = 0; i < kallsyms->num_symtab; i++) { + +			if (kallsyms->symtab[i].st_shndx == SHN_UNDEF) +				continue; +  			ret = fn(data, symname(kallsyms, i),  				 mod, kallsyms->symtab[i].st_value);  			if (ret != 0) |