diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 58 | 
1 files changed, 39 insertions, 19 deletions
| diff --git a/kernel/module.c b/kernel/module.c index 12afc5a3ddd3..f82386bd9ee9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -880,11 +880,23 @@ static int try_to_force_load(struct module *mod, const char *reason)  }  #ifdef CONFIG_MODVERSIONS +/* If the arch applies (non-zero) relocations to kernel kcrctab, unapply it. */ +static unsigned long maybe_relocated(unsigned long crc, +				     const struct module *crc_owner) +{ +#ifdef ARCH_RELOCATES_KCRCTAB +	if (crc_owner == NULL) +		return crc - (unsigned long)reloc_start; +#endif +	return crc; +} +  static int check_version(Elf_Shdr *sechdrs,  			 unsigned int versindex,  			 const char *symname,  			 struct module *mod,  -			 const unsigned long *crc) +			 const unsigned long *crc, +			 const struct module *crc_owner)  {  	unsigned int i, num_versions;  	struct modversion_info *versions; @@ -905,10 +917,10 @@ static int check_version(Elf_Shdr *sechdrs,  		if (strcmp(versions[i].name, symname) != 0)  			continue; -		if (versions[i].crc == *crc) +		if (versions[i].crc == maybe_relocated(*crc, crc_owner))  			return 1;  		DEBUGP("Found checksum %lX vs module %lX\n", -		       *crc, versions[i].crc); +		       maybe_relocated(*crc, crc_owner), versions[i].crc);  		goto bad_version;  	} @@ -931,7 +943,8 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,  	if (!find_symbol(MODULE_SYMBOL_PREFIX "module_layout", NULL,  			 &crc, true, false))  		BUG(); -	return check_version(sechdrs, versindex, "module_layout", mod, crc); +	return check_version(sechdrs, versindex, "module_layout", mod, crc, +			     NULL);  }  /* First part is kernel version, which we ignore if module has crcs. */ @@ -949,7 +962,8 @@ static inline int check_version(Elf_Shdr *sechdrs,  				unsigned int versindex,  				const char *symname,  				struct module *mod,  -				const unsigned long *crc) +				const unsigned long *crc, +				const struct module *crc_owner)  {  	return 1;  } @@ -984,8 +998,8 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,  	/* use_module can fail due to OOM,  	   or module initialization or unloading */  	if (sym) { -		if (!check_version(sechdrs, versindex, name, mod, crc) || -		    !use_module(mod, owner)) +		if (!check_version(sechdrs, versindex, name, mod, crc, owner) +		    || !use_module(mod, owner))  			sym = NULL;  	}  	return sym; @@ -996,6 +1010,12 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,   * J. Corbet <corbet@lwn.net>   */  #if defined(CONFIG_KALLSYMS) && defined(CONFIG_SYSFS) + +static inline bool sect_empty(const Elf_Shdr *sect) +{ +	return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; +} +  struct module_sect_attr  {  	struct module_attribute mattr; @@ -1037,8 +1057,7 @@ static void add_sect_attrs(struct module *mod, unsigned int nsect,  	/* Count loaded sections and allocate structures */  	for (i = 0; i < nsect; i++) -		if (sechdrs[i].sh_flags & SHF_ALLOC -		    && sechdrs[i].sh_size) +		if (!sect_empty(&sechdrs[i]))  			nloaded++;  	size[0] = ALIGN(sizeof(*sect_attrs)  			+ nloaded * sizeof(sect_attrs->attrs[0]), @@ -1056,9 +1075,7 @@ static void add_sect_attrs(struct module *mod, unsigned int nsect,  	sattr = §_attrs->attrs[0];  	gattr = §_attrs->grp.attrs[0];  	for (i = 0; i < nsect; i++) { -		if (! (sechdrs[i].sh_flags & SHF_ALLOC)) -			continue; -		if (!sechdrs[i].sh_size) +		if (sect_empty(&sechdrs[i]))  			continue;  		sattr->address = sechdrs[i].sh_addr;  		sattr->name = kstrdup(secstrings + sechdrs[i].sh_name, @@ -1142,7 +1159,7 @@ static void add_notes_attrs(struct module *mod, unsigned int nsect,  	/* Count notes sections and allocate structures.  */  	notes = 0;  	for (i = 0; i < nsect; i++) -		if ((sechdrs[i].sh_flags & SHF_ALLOC) && +		if (!sect_empty(&sechdrs[i]) &&  		    (sechdrs[i].sh_type == SHT_NOTE))  			++notes; @@ -1158,7 +1175,7 @@ static void add_notes_attrs(struct module *mod, unsigned int nsect,  	notes_attrs->notes = notes;  	nattr = ¬es_attrs->attrs[0];  	for (loaded = i = 0; i < nsect; ++i) { -		if (!(sechdrs[i].sh_flags & SHF_ALLOC)) +		if (sect_empty(&sechdrs[i]))  			continue;  		if (sechdrs[i].sh_type == SHT_NOTE) {  			nattr->attr.name = mod->sect_attrs->attrs[loaded].name; @@ -1896,9 +1913,7 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,  	unsigned int i;  	/* only scan the sections containing data */ -	kmemleak_scan_area(mod->module_core, (unsigned long)mod - -			   (unsigned long)mod->module_core, -			   sizeof(struct module), GFP_KERNEL); +	kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);  	for (i = 1; i < hdr->e_shnum; i++) {  		if (!(sechdrs[i].sh_flags & SHF_ALLOC)) @@ -1907,8 +1922,7 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,  		    && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0)  			continue; -		kmemleak_scan_area(mod->module_core, sechdrs[i].sh_addr - -				   (unsigned long)mod->module_core, +		kmemleak_scan_area((void *)sechdrs[i].sh_addr,  				   sechdrs[i].sh_size, GFP_KERNEL);  	}  } @@ -2236,6 +2250,12 @@ static noinline struct module *load_module(void __user *umod,  					 "_ftrace_events",  					 sizeof(*mod->trace_events),  					 &mod->num_trace_events); +	/* +	 * This section contains pointers to allocated objects in the trace +	 * code and not scanning it leads to false positives. +	 */ +	kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) * +			   mod->num_trace_events, GFP_KERNEL);  #endif  #ifdef CONFIG_FTRACE_MCOUNT_RECORD  	/* sechdrs[0].sh_size is always zero */ | 
