diff options
author | Alexey Dobriyan <adobriyan@sw.ru> | 2007-05-08 03:28:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:08 -0400 |
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); | |||
370 | struct module *__module_text_address(unsigned long addr); | 370 | struct module *__module_text_address(unsigned long addr); |
371 | int is_module_address(unsigned long addr); | 371 | int is_module_address(unsigned long addr); |
372 | 372 | ||
373 | /* Returns module and fills in value, defined and namebuf, or NULL if | 373 | /* Returns 0 and fills in value, defined and namebuf, or -ERANGE if |
374 | symnum out of range. */ | 374 | symnum out of range. */ |
375 | struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, | 375 | int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, |
376 | char *type, char *name); | 376 | char *name, char *module_name, int *exported); |
377 | 377 | ||
378 | /* Look for this name: can be of form module:name. */ | 378 | /* Look for this name: can be of form module:name. */ |
379 | unsigned long module_kallsyms_lookup_name(const char *name); | 379 | unsigned long module_kallsyms_lookup_name(const char *name); |
380 | 380 | ||
381 | int is_exported(const char *name, const struct module *mod); | ||
382 | |||
383 | extern void __module_put_and_exit(struct module *mod, long code) | 381 | extern void __module_put_and_exit(struct module *mod, long code) |
384 | __attribute__((noreturn)); | 382 | __attribute__((noreturn)); |
385 | #define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code); | 383 | #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, | |||
527 | return NULL; | 525 | return NULL; |
528 | } | 526 | } |
529 | 527 | ||
530 | static inline struct module *module_get_kallsym(unsigned int symnum, | 528 | static inline int module_get_kallsym(unsigned int symnum, unsigned long *value, |
531 | unsigned long *value, | 529 | char *type, char *name, |
532 | char *type, char *name) | 530 | char *module_name, int *exported) |
533 | { | 531 | { |
534 | return NULL; | 532 | return -ERANGE; |
535 | } | 533 | } |
536 | 534 | ||
537 | static inline unsigned long module_kallsyms_lookup_name(const char *name) | 535 | 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) | |||
539 | return 0; | 537 | return 0; |
540 | } | 538 | } |
541 | 539 | ||
542 | static inline int is_exported(const char *name, const struct module *mod) | ||
543 | { | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static inline int register_module_notifier(struct notifier_block * nb) | 540 | static inline int register_module_notifier(struct notifier_block * nb) |
548 | { | 541 | { |
549 | /* no events will happen anyway, so this can always succeed */ | 542 | /* 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) | |||
301 | struct kallsym_iter | 301 | struct kallsym_iter |
302 | { | 302 | { |
303 | loff_t pos; | 303 | loff_t pos; |
304 | struct module *owner; | ||
305 | unsigned long value; | 304 | unsigned long value; |
306 | unsigned int nameoff; /* If iterating in core kernel symbols */ | 305 | unsigned int nameoff; /* If iterating in core kernel symbols */ |
307 | char type; | 306 | char type; |
308 | char name[KSYM_NAME_LEN+1]; | 307 | char name[KSYM_NAME_LEN+1]; |
308 | char module_name[MODULE_NAME_LEN + 1]; | ||
309 | int exported; | ||
309 | }; | 310 | }; |
310 | 311 | ||
311 | static int get_ksymbol_mod(struct kallsym_iter *iter) | 312 | static int get_ksymbol_mod(struct kallsym_iter *iter) |
312 | { | 313 | { |
313 | iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms, | 314 | if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value, |
314 | &iter->value, &iter->type, | 315 | &iter->type, iter->name, iter->module_name, |
315 | iter->name); | 316 | &iter->exported) < 0) |
316 | if (iter->owner == NULL) | ||
317 | return 0; | 317 | return 0; |
318 | |||
319 | /* Label it "global" if it is exported, "local" if not exported. */ | ||
320 | iter->type = is_exported(iter->name, iter->owner) | ||
321 | ? toupper(iter->type) : tolower(iter->type); | ||
322 | |||
323 | return 1; | 318 | return 1; |
324 | } | 319 | } |
325 | 320 | ||
@@ -328,7 +323,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter) | |||
328 | { | 323 | { |
329 | unsigned off = iter->nameoff; | 324 | unsigned off = iter->nameoff; |
330 | 325 | ||
331 | iter->owner = NULL; | 326 | iter->module_name[0] = '\0'; |
332 | iter->value = kallsyms_addresses[iter->pos]; | 327 | iter->value = kallsyms_addresses[iter->pos]; |
333 | 328 | ||
334 | iter->type = kallsyms_get_symbol_type(off); | 329 | iter->type = kallsyms_get_symbol_type(off); |
@@ -392,12 +387,17 @@ static int s_show(struct seq_file *m, void *p) | |||
392 | if (!iter->name[0]) | 387 | if (!iter->name[0]) |
393 | return 0; | 388 | return 0; |
394 | 389 | ||
395 | if (iter->owner) | 390 | if (iter->module_name[0]) { |
391 | char type; | ||
392 | |||
393 | /* Label it "global" if it is exported, | ||
394 | * "local" if not exported. */ | ||
395 | type = iter->exported ? toupper(iter->type) : | ||
396 | tolower(iter->type); | ||
396 | seq_printf(m, "%0*lx %c %s\t[%s]\n", | 397 | seq_printf(m, "%0*lx %c %s\t[%s]\n", |
397 | (int)(2*sizeof(void*)), | 398 | (int)(2*sizeof(void*)), |
398 | iter->value, iter->type, iter->name, | 399 | iter->value, type, iter->name, iter->module_name); |
399 | module_name(iter->owner)); | 400 | } else |
400 | else | ||
401 | seq_printf(m, "%0*lx %c %s\n", | 401 | seq_printf(m, "%0*lx %c %s\n", |
402 | (int)(2*sizeof(void*)), | 402 | (int)(2*sizeof(void*)), |
403 | iter->value, iter->type, iter->name); | 403 | 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, | |||
1472 | } | 1472 | } |
1473 | 1473 | ||
1474 | #ifdef CONFIG_KALLSYMS | 1474 | #ifdef CONFIG_KALLSYMS |
1475 | int is_exported(const char *name, const struct module *mod) | 1475 | static int is_exported(const char *name, const struct module *mod) |
1476 | { | 1476 | { |
1477 | if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab)) | 1477 | if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab)) |
1478 | return 1; | 1478 | return 1; |
@@ -2124,8 +2124,8 @@ const char *module_address_lookup(unsigned long addr, | |||
2124 | return NULL; | 2124 | return NULL; |
2125 | } | 2125 | } |
2126 | 2126 | ||
2127 | struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, | 2127 | int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, |
2128 | char *type, char *name) | 2128 | char *name, char *module_name, int *exported) |
2129 | { | 2129 | { |
2130 | struct module *mod; | 2130 | struct module *mod; |
2131 | 2131 | ||
@@ -2136,13 +2136,15 @@ struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, | |||
2136 | *type = mod->symtab[symnum].st_info; | 2136 | *type = mod->symtab[symnum].st_info; |
2137 | strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, | 2137 | strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, |
2138 | KSYM_NAME_LEN + 1); | 2138 | KSYM_NAME_LEN + 1); |
2139 | strlcpy(module_name, mod->name, MODULE_NAME_LEN + 1); | ||
2140 | *exported = is_exported(name, mod); | ||
2139 | mutex_unlock(&module_mutex); | 2141 | mutex_unlock(&module_mutex); |
2140 | return mod; | 2142 | return 0; |
2141 | } | 2143 | } |
2142 | symnum -= mod->num_symtab; | 2144 | symnum -= mod->num_symtab; |
2143 | } | 2145 | } |
2144 | mutex_unlock(&module_mutex); | 2146 | mutex_unlock(&module_mutex); |
2145 | return NULL; | 2147 | return -ERANGE; |
2146 | } | 2148 | } |
2147 | 2149 | ||
2148 | static unsigned long mod_find_symname(struct module *mod, const char *name) | 2150 | static unsigned long mod_find_symname(struct module *mod, const char *name) |