diff options
| author | Alexey Dobriyan <adobriyan@sw.ru> | 2007-05-08 00:28:39 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 11:15:08 -0700 | 
| commit | ea07890a680273b25127129fb555aac0d9324bea (patch) | |
| tree | b0742aa5dd90792dc10be3563c1181582d0f5d9e | |
| parent | ae84e324709d6320ed8c1fd7b1736fcbaf26df95 (diff) | |
Fix race between rmmod and cat /proc/kallsyms
module_get_kallsym() leaks "struct module *" outside of module_mutex which is
no-no, because module can dissapear right after mutex unlock.
Copy all needed information from inside module_mutex into caller-supplied
space.
[bunk@stusta.de: is_exported() can now become static]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/module.h | 21 | ||||
| -rw-r--r-- | kernel/kallsyms.c | 30 | ||||
| -rw-r--r-- | kernel/module.c | 12 | 
3 files changed, 29 insertions, 34 deletions
| diff --git a/include/linux/module.h b/include/linux/module.h index 799930216626..58d5a10cdf0d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -370,16 +370,14 @@ struct module *module_text_address(unsigned long addr);  struct module *__module_text_address(unsigned long addr);  int is_module_address(unsigned long addr); -/* Returns module and fills in value, defined and namebuf, or NULL if +/* Returns 0 and fills in value, defined and namebuf, or -ERANGE if     symnum out of range. */ -struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, -				char *type, char *name); +int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, +			char *name, char *module_name, int *exported);  /* Look for this name: can be of form module:name. */  unsigned long module_kallsyms_lookup_name(const char *name); -int is_exported(const char *name, const struct module *mod); -  extern void __module_put_and_exit(struct module *mod, long code)  	__attribute__((noreturn));  #define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code); @@ -527,11 +525,11 @@ static inline const char *module_address_lookup(unsigned long addr,  	return NULL;  } -static inline struct module *module_get_kallsym(unsigned int symnum, -						unsigned long *value, -						char *type, char *name) +static inline int module_get_kallsym(unsigned int symnum, unsigned long *value, +					char *type, char *name, +					char *module_name, int *exported)  { -	return NULL; +	return -ERANGE;  }  static inline unsigned long module_kallsyms_lookup_name(const char *name) @@ -539,11 +537,6 @@ static inline unsigned long module_kallsyms_lookup_name(const char *name)  	return 0;  } -static inline int is_exported(const char *name, const struct module *mod) -{ -	return 0; -} -  static inline int register_module_notifier(struct notifier_block * nb)  {  	/* no events will happen anyway, so this can always succeed */ diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index d086c91d44ed..f1ea6f66ac6c 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -301,25 +301,20 @@ void __print_symbol(const char *fmt, unsigned long address)  struct kallsym_iter  {  	loff_t pos; -	struct module *owner;  	unsigned long value;  	unsigned int nameoff; /* If iterating in core kernel symbols */  	char type;  	char name[KSYM_NAME_LEN+1]; +	char module_name[MODULE_NAME_LEN + 1]; +	int exported;  };  static int get_ksymbol_mod(struct kallsym_iter *iter)  { -	iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms, -					 &iter->value, &iter->type, -					 iter->name); -	if (iter->owner == NULL) +	if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value, +				&iter->type, iter->name, iter->module_name, +				&iter->exported) < 0)  		return 0; - -	/* Label it "global" if it is exported, "local" if not exported. */ -	iter->type = is_exported(iter->name, iter->owner) -		? toupper(iter->type) : tolower(iter->type); -  	return 1;  } @@ -328,7 +323,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter)  {  	unsigned off = iter->nameoff; -	iter->owner = NULL; +	iter->module_name[0] = '\0';  	iter->value = kallsyms_addresses[iter->pos];  	iter->type = kallsyms_get_symbol_type(off); @@ -392,12 +387,17 @@ static int s_show(struct seq_file *m, void *p)  	if (!iter->name[0])  		return 0; -	if (iter->owner) +	if (iter->module_name[0]) { +		char type; + +		/* Label it "global" if it is exported, +		 * "local" if not exported. */ +		type = iter->exported ? toupper(iter->type) : +					tolower(iter->type);  		seq_printf(m, "%0*lx %c %s\t[%s]\n",  			   (int)(2*sizeof(void*)), -			   iter->value, iter->type, iter->name, -			   module_name(iter->owner)); -	else +			   iter->value, type, iter->name, iter->module_name); +	} else  		seq_printf(m, "%0*lx %c %s\n",  			   (int)(2*sizeof(void*)),  			   iter->value, iter->type, iter->name); diff --git a/kernel/module.c b/kernel/module.c index 43a529a1fa48..5ee65994a3bc 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1472,7 +1472,7 @@ static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs,  }  #ifdef CONFIG_KALLSYMS -int is_exported(const char *name, const struct module *mod) +static int is_exported(const char *name, const struct module *mod)  {  	if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))  		return 1; @@ -2124,8 +2124,8 @@ const char *module_address_lookup(unsigned long addr,  	return NULL;  } -struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, -				char *type, char *name) +int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, +			char *name, char *module_name, int *exported)  {  	struct module *mod; @@ -2136,13 +2136,15 @@ struct module *module_get_kallsym(unsigned int symnum, unsigned long *value,  			*type = mod->symtab[symnum].st_info;  			strlcpy(name, mod->strtab + mod->symtab[symnum].st_name,  				KSYM_NAME_LEN + 1); +			strlcpy(module_name, mod->name, MODULE_NAME_LEN + 1); +			*exported = is_exported(name, mod);  			mutex_unlock(&module_mutex); -			return mod; +			return 0;  		}  		symnum -= mod->num_symtab;  	}  	mutex_unlock(&module_mutex); -	return NULL; +	return -ERANGE;  }  static unsigned long mod_find_symname(struct module *mod, const char *name) | 
