From: Rusty Russell store_stackinfo() does an unlocked module list walk during normal runtime which opens up a race with the module load/unload code. This can be triggered by simply unloading and loading a module in a loop with CONFIG_DEBUG_PAGEALLOC resulting in store_stackinfo() tripping over bad list pointers. kernel_text_address doesn't take any locks, because during an OOPS we don't want to deadlock. Rename that to __kernel_text_address, and make kernel_text_address take the lock. Signed-off-by: Zwane Mwaikambo Signed-off-by: Rusty Russell (modified) Signed-off-by: Andrew Morton --- 25-akpm/arch/i386/kernel/traps.c | 2 +- 25-akpm/arch/m68k/kernel/traps.c | 2 +- 25-akpm/arch/mips/kernel/traps.c | 2 +- 25-akpm/arch/parisc/kernel/traps.c | 2 +- 25-akpm/arch/um/kernel/sysrq.c | 2 +- 25-akpm/arch/x86_64/kernel/traps.c | 6 +++--- 25-akpm/include/linux/kernel.h | 1 + 25-akpm/include/linux/module.h | 9 ++++++++- 25-akpm/kernel/extable.c | 15 ++++++++++++++- 25-akpm/kernel/module.c | 14 +++++++++++++- 10 files changed, 44 insertions(+), 11 deletions(-) diff -puN arch/i386/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 arch/i386/kernel/traps.c --- 25/arch/i386/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.957640048 -0700 +++ 25-akpm/arch/i386/kernel/traps.c 2004-06-24 00:37:15.975637312 -0700 @@ -158,7 +158,7 @@ static void print_context_stack(struct t while (!kstack_end(stack)) { addr = *stack++; - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { printk(" [<%08lx>]", addr); print_symbol(" %s", addr); printk("\n"); diff -puN arch/m68k/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 arch/m68k/kernel/traps.c --- 25/arch/m68k/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.959639744 -0700 +++ 25-akpm/arch/m68k/kernel/traps.c 2004-06-24 00:37:15.976637160 -0700 @@ -911,7 +911,7 @@ void show_trace(unsigned long *stack) * down the cause of the crash will be able to figure * out the call path that was taken. */ - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { #ifndef CONFIG_KALLSYMS if (i % 5 == 0) printk("\n "); diff -puN arch/mips/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 arch/mips/kernel/traps.c --- 25/arch/mips/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.961639440 -0700 +++ 25-akpm/arch/mips/kernel/traps.c 2004-06-24 00:37:15.977637008 -0700 @@ -118,7 +118,7 @@ void show_trace(struct task_struct *task #endif while (!kstack_end(stack)) { addr = *stack++; - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { printk(" [<%0*lx>] ", field, addr); print_symbol("%s\n", addr); } diff -puN arch/parisc/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 arch/parisc/kernel/traps.c --- 25/arch/parisc/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.962639288 -0700 +++ 25-akpm/arch/parisc/kernel/traps.c 2004-06-24 00:37:15.978636856 -0700 @@ -188,7 +188,7 @@ void show_trace(struct task_struct *task * down the cause of the crash will be able to figure * out the call path that was taken. */ - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { printk(" [<" RFMT ">] ", addr); #ifdef CONFIG_KALLSYMS print_symbol("%s\n", addr); diff -puN arch/um/kernel/sysrq.c~fix-module_text_address-store_stackinfo-race-2 arch/um/kernel/sysrq.c --- 25/arch/um/kernel/sysrq.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.964638984 -0700 +++ 25-akpm/arch/um/kernel/sysrq.c 2004-06-24 00:37:15.979636704 -0700 @@ -23,7 +23,7 @@ void show_trace(unsigned long * stack) i = 1; while (((long) stack & (THREAD_SIZE-1)) != 0) { addr = *stack++; - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { if (i && ((i % 6) == 0)) printk("\n "); printk("[<%08lx>] ", addr); diff -puN arch/x86_64/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 arch/x86_64/kernel/traps.c --- 25/arch/x86_64/kernel/traps.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.965638832 -0700 +++ 25-akpm/arch/x86_64/kernel/traps.c 2004-06-24 00:37:15.979636704 -0700 @@ -143,7 +143,7 @@ void show_trace(unsigned long *stack) if (estack_end) { while (stack < estack_end) { addr = *stack++; - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { i += printk_address(addr); i += printk(" "); if (i > 50) { @@ -172,7 +172,7 @@ void show_trace(unsigned long *stack) * down the cause of the crash will be able to figure * out the call path that was taken. */ - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { i += printk_address(addr); i += printk(" "); if (i > 50) { @@ -188,7 +188,7 @@ void show_trace(unsigned long *stack) while (((long) stack & (THREAD_SIZE-1)) != 0) { addr = *stack++; - if (kernel_text_address(addr)) { + if (__kernel_text_address(addr)) { i += printk_address(addr); i += printk(" "); if (i > 50) { diff -puN include/linux/kernel.h~fix-module_text_address-store_stackinfo-race-2 include/linux/kernel.h --- 25/include/linux/kernel.h~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.967638528 -0700 +++ 25-akpm/include/linux/kernel.h 2004-06-24 00:37:15.980636552 -0700 @@ -93,6 +93,7 @@ extern int get_option(char **str, int *p extern char *get_options(const char *str, int nints, int *ints); extern unsigned long long memparse(char *ptr, char **retptr); +extern int __kernel_text_address(unsigned long addr); extern int kernel_text_address(unsigned long addr); extern int session_of_pgrp(int pgrp); diff -puN include/linux/module.h~fix-module_text_address-store_stackinfo-race-2 include/linux/module.h --- 25/include/linux/module.h~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.968638376 -0700 +++ 25-akpm/include/linux/module.h 2004-06-24 00:37:15.981636400 -0700 @@ -335,8 +335,9 @@ static inline int module_is_live(struct return mod->state != MODULE_STATE_GOING; } -/* Is this address in a module? */ +/* Is this address in a module? (second is with no locks, for oops) */ struct module *module_text_address(unsigned long addr); +struct module *__module_text_address(unsigned long addr); /* Returns module and fills in value, defined and namebuf, or NULL if symnum out of range. */ @@ -462,6 +463,12 @@ static inline struct module *module_text return NULL; } +/* Is this address in a module? (don't take a lock, we're oopsing) */ +static inline struct module *__module_text_address(unsigned long addr) +{ + return NULL; +} + /* Get/put a kernel symbol (calls should be symmetric) */ #define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); }) #define symbol_put(x) do { } while(0) diff -puN kernel/extable.c~fix-module_text_address-store_stackinfo-race-2 kernel/extable.c --- 25/kernel/extable.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.969638224 -0700 +++ 25-akpm/kernel/extable.c 2004-06-24 00:37:15.982636248 -0700 @@ -40,7 +40,7 @@ const struct exception_table_entry *sear return e; } -int kernel_text_address(unsigned long addr) +static int core_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) @@ -49,6 +49,19 @@ int kernel_text_address(unsigned long ad if (addr >= (unsigned long)_sinittext && addr <= (unsigned long)_einittext) return 1; + return 0; +} +int __kernel_text_address(unsigned long addr) +{ + if (core_kernel_text(addr)) + return 1; + return __module_text_address(addr) != NULL; +} + +int kernel_text_address(unsigned long addr) +{ + if (core_kernel_text(addr)) + return 1; return module_text_address(addr) != NULL; } diff -puN kernel/module.c~fix-module_text_address-store_stackinfo-race-2 kernel/module.c --- 25/kernel/module.c~fix-module_text_address-store_stackinfo-race-2 2004-06-24 00:37:15.971637920 -0700 +++ 25-akpm/kernel/module.c 2004-06-24 00:37:15.983636096 -0700 @@ -2128,7 +2128,7 @@ const struct exception_table_entry *sear } /* Is this a valid kernel address? We don't grab the lock: we are oopsing. */ -struct module *module_text_address(unsigned long addr) +struct module *__module_text_address(unsigned long addr) { struct module *mod; @@ -2139,6 +2139,18 @@ struct module *module_text_address(unsig return NULL; } +struct module *module_text_address(unsigned long addr) +{ + struct module *mod; + unsigned long flags; + + spin_lock_irqsave(&modlist_lock, flags); + mod = __module_text_address(addr); + spin_unlock_irqrestore(&modlist_lock, flags); + + return mod; +} + /* Don't grab lock, we're oopsing. */ void print_modules(void) { _