Commit 9d65cb4a1718a072898c7a57a3bc61b2dc4bcd4d

Authored by Alexey Dobriyan
Committed by Linus Torvalds
1 parent ffb4512276

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

... ... @@ -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,
... ... @@ -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 {
... ... @@ -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)