Commit 9d65cb4a1718a072898c7a57a3bc61b2dc4bcd4d
Committed by
Linus Torvalds
1 parent
ffb4512276
Exists in
master
and in
20 other branches
Fix race between cat /proc/*/wchan and rmmod et al
kallsyms_lookup() can go iterating over modules list unprotected which is OK for emergency situations (oops), but not OK for regular stuff like /proc/*/wchan. Introduce lookup_symbol_name()/lookup_module_symbol_name() which copy symbol name into caller-supplied buffer or return -ERANGE. All copying is done with module_mutex held, so... Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru> Cc: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 7 changed files with 66 additions and 19 deletions Side-by-side Diff
fs/proc/base.c
... | ... | @@ -278,16 +278,15 @@ |
278 | 278 | */ |
279 | 279 | static int proc_pid_wchan(struct task_struct *task, char *buffer) |
280 | 280 | { |
281 | - const char *sym_name; | |
282 | 281 | unsigned long wchan; |
283 | - char namebuf[KSYM_NAME_LEN+1]; | |
282 | + char symname[KSYM_NAME_LEN+1]; | |
284 | 283 | |
285 | 284 | wchan = get_wchan(task); |
286 | 285 | |
287 | - sym_name = kallsyms_lookup(wchan, NULL, NULL, NULL, namebuf); | |
288 | - if (sym_name) | |
289 | - return sprintf(buffer, "%s", sym_name); | |
290 | - return sprintf(buffer, "%lu", wchan); | |
286 | + if (lookup_symbol_name(wchan, symname) < 0) | |
287 | + return sprintf(buffer, "%lu", wchan); | |
288 | + else | |
289 | + return sprintf(buffer, "%s", symname); | |
291 | 290 | } |
292 | 291 | #endif /* CONFIG_KALLSYMS */ |
293 | 292 |
include/linux/kallsyms.h
... | ... | @@ -30,6 +30,8 @@ |
30 | 30 | /* Look up a kernel symbol and print it to the kernel messages. */ |
31 | 31 | extern void __print_symbol(const char *fmt, unsigned long address); |
32 | 32 | |
33 | +int lookup_symbol_name(unsigned long addr, char *symname); | |
34 | + | |
33 | 35 | #else /* !CONFIG_KALLSYMS */ |
34 | 36 | |
35 | 37 | static inline unsigned long kallsyms_lookup_name(const char *name) |
... | ... | @@ -56,6 +58,11 @@ |
56 | 58 | { |
57 | 59 | *buffer = '\0'; |
58 | 60 | return 0; |
61 | +} | |
62 | + | |
63 | +static inline int lookup_symbol_name(unsigned long addr, char *symname) | |
64 | +{ | |
65 | + return -ERANGE; | |
59 | 66 | } |
60 | 67 | |
61 | 68 | /* Stupid that this does nothing, but I didn't create this mess. */ |
include/linux/module.h
... | ... | @@ -454,6 +454,7 @@ |
454 | 454 | unsigned long *symbolsize, |
455 | 455 | unsigned long *offset, |
456 | 456 | char **modname); |
457 | +int lookup_module_symbol_name(unsigned long addr, char *symname); | |
457 | 458 | |
458 | 459 | /* For extable.c to search modules' exception tables. */ |
459 | 460 | const struct exception_table_entry *search_module_extables(unsigned long addr); |
... | ... | @@ -523,6 +524,11 @@ |
523 | 524 | char **modname) |
524 | 525 | { |
525 | 526 | return NULL; |
527 | +} | |
528 | + | |
529 | +static inline int lookup_module_symbol_name(unsigned long addr, char *symname) | |
530 | +{ | |
531 | + return -ERANGE; | |
526 | 532 | } |
527 | 533 | |
528 | 534 | static inline int module_get_kallsym(unsigned int symnum, unsigned long *value, |
kernel/kallsyms.c
... | ... | @@ -269,6 +269,23 @@ |
269 | 269 | return NULL; |
270 | 270 | } |
271 | 271 | |
272 | +int lookup_symbol_name(unsigned long addr, char *symname) | |
273 | +{ | |
274 | + symname[0] = '\0'; | |
275 | + symname[KSYM_NAME_LEN] = '\0'; | |
276 | + | |
277 | + if (is_ksym_addr(addr)) { | |
278 | + unsigned long pos; | |
279 | + | |
280 | + pos = get_symbol_pos(addr, NULL, NULL); | |
281 | + /* Grab name */ | |
282 | + kallsyms_expand_symbol(get_symbol_offset(pos), symname); | |
283 | + return 0; | |
284 | + } | |
285 | + /* see if it's in a module */ | |
286 | + return lookup_module_symbol_name(addr, symname); | |
287 | +} | |
288 | + | |
272 | 289 | /* Look up a kernel symbol and return it in a text buffer. */ |
273 | 290 | int sprint_symbol(char *buffer, unsigned long address) |
274 | 291 | { |
kernel/module.c
... | ... | @@ -2126,6 +2126,29 @@ |
2126 | 2126 | return NULL; |
2127 | 2127 | } |
2128 | 2128 | |
2129 | +int lookup_module_symbol_name(unsigned long addr, char *symname) | |
2130 | +{ | |
2131 | + struct module *mod; | |
2132 | + | |
2133 | + mutex_lock(&module_mutex); | |
2134 | + list_for_each_entry(mod, &modules, list) { | |
2135 | + if (within(addr, mod->module_init, mod->init_size) || | |
2136 | + within(addr, mod->module_core, mod->core_size)) { | |
2137 | + const char *sym; | |
2138 | + | |
2139 | + sym = get_ksymbol(mod, addr, NULL, NULL); | |
2140 | + if (!sym) | |
2141 | + goto out; | |
2142 | + strlcpy(symname, sym, KSYM_NAME_LEN + 1); | |
2143 | + mutex_unlock(&module_mutex); | |
2144 | + return 0; | |
2145 | + } | |
2146 | + } | |
2147 | +out: | |
2148 | + mutex_unlock(&module_mutex); | |
2149 | + return -ERANGE; | |
2150 | +} | |
2151 | + | |
2129 | 2152 | int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, |
2130 | 2153 | char *name, char *module_name, int *exported) |
2131 | 2154 | { |
kernel/time/timer_list.c
... | ... | @@ -38,15 +38,12 @@ |
38 | 38 | |
39 | 39 | static void print_name_offset(struct seq_file *m, void *sym) |
40 | 40 | { |
41 | - unsigned long addr = (unsigned long)sym; | |
42 | - char namebuf[KSYM_NAME_LEN+1]; | |
43 | - const char *sym_name; | |
41 | + char symname[KSYM_NAME_LEN+1]; | |
44 | 42 | |
45 | - sym_name = kallsyms_lookup(addr, NULL, NULL, NULL, namebuf); | |
46 | - if (sym_name) | |
47 | - SEQ_printf(m, "%s", sym_name); | |
48 | - else | |
43 | + if (lookup_symbol_name((unsigned long)sym, symname) < 0) | |
49 | 44 | SEQ_printf(m, "<%p>", sym); |
45 | + else | |
46 | + SEQ_printf(m, "%s", symname); | |
50 | 47 | } |
51 | 48 | |
52 | 49 | static void |
kernel/time/timer_stats.c
... | ... | @@ -257,14 +257,12 @@ |
257 | 257 | |
258 | 258 | static void print_name_offset(struct seq_file *m, unsigned long addr) |
259 | 259 | { |
260 | - char namebuf[KSYM_NAME_LEN+1]; | |
261 | - const char *sym_name; | |
260 | + char symname[KSYM_NAME_LEN+1]; | |
262 | 261 | |
263 | - sym_name = kallsyms_lookup(addr, NULL, NULL, NULL, namebuf); | |
264 | - if (sym_name) | |
265 | - seq_printf(m, "%s", sym_name); | |
266 | - else | |
262 | + if (lookup_symbol_name(addr, symname) < 0) | |
267 | 263 | seq_printf(m, "<%p>", (void *)addr); |
264 | + else | |
265 | + seq_printf(m, "%s", symname); | |
268 | 266 | } |
269 | 267 | |
270 | 268 | static int tstats_show(struct seq_file *m, void *v) |