Commit 969eb7b8 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] Fix race between CONFIG_DEBUG_SLABALLOC and modules

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: default avatarZwane Mwaikambo <zwane@fsmlabs.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (modified)
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 12d9986b
...@@ -124,7 +124,7 @@ void print_context_stack(struct task_struct *task, unsigned long *stack, ...@@ -124,7 +124,7 @@ void print_context_stack(struct task_struct *task, unsigned long *stack,
while (!kstack_end(stack)) { while (!kstack_end(stack)) {
addr = *stack++; addr = *stack++;
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
printk(" [<%08lx>] ", addr); printk(" [<%08lx>] ", addr);
print_symbol("%s\n", addr); print_symbol("%s\n", addr);
} }
......
...@@ -911,7 +911,7 @@ void show_trace(unsigned long *stack) ...@@ -911,7 +911,7 @@ void show_trace(unsigned long *stack)
* down the cause of the crash will be able to figure * down the cause of the crash will be able to figure
* out the call path that was taken. * out the call path that was taken.
*/ */
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
#ifndef CONFIG_KALLSYMS #ifndef CONFIG_KALLSYMS
if (i % 5 == 0) if (i % 5 == 0)
printk("\n "); printk("\n ");
......
...@@ -118,7 +118,7 @@ void show_trace(struct task_struct *task, unsigned long *stack) ...@@ -118,7 +118,7 @@ void show_trace(struct task_struct *task, unsigned long *stack)
#endif #endif
while (!kstack_end(stack)) { while (!kstack_end(stack)) {
addr = *stack++; addr = *stack++;
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
printk(" [<%0*lx>] ", field, addr); printk(" [<%0*lx>] ", field, addr);
print_symbol("%s\n", addr); print_symbol("%s\n", addr);
} }
......
...@@ -188,7 +188,7 @@ void show_trace(struct task_struct *task, unsigned long *stack) ...@@ -188,7 +188,7 @@ void show_trace(struct task_struct *task, unsigned long *stack)
* down the cause of the crash will be able to figure * down the cause of the crash will be able to figure
* out the call path that was taken. * out the call path that was taken.
*/ */
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
printk(" [<" RFMT ">] ", addr); printk(" [<" RFMT ">] ", addr);
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
print_symbol("%s\n", addr); print_symbol("%s\n", addr);
......
...@@ -23,7 +23,7 @@ void show_trace(unsigned long * stack) ...@@ -23,7 +23,7 @@ void show_trace(unsigned long * stack)
i = 1; i = 1;
while (((long) stack & (THREAD_SIZE-1)) != 0) { while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack++; addr = *stack++;
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
if (i && ((i % 6) == 0)) if (i && ((i % 6) == 0))
printk("\n "); printk("\n ");
printk("[<%08lx>] ", addr); printk("[<%08lx>] ", addr);
......
...@@ -140,7 +140,7 @@ void show_trace(unsigned long *stack) ...@@ -140,7 +140,7 @@ void show_trace(unsigned long *stack)
if (estack_end) { if (estack_end) {
while (stack < estack_end) { while (stack < estack_end) {
addr = *stack++; addr = *stack++;
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
i += printk_address(addr); i += printk_address(addr);
i += printk(" "); i += printk(" ");
if (i > 50) { if (i > 50) {
...@@ -169,7 +169,7 @@ void show_trace(unsigned long *stack) ...@@ -169,7 +169,7 @@ void show_trace(unsigned long *stack)
* down the cause of the crash will be able to figure * down the cause of the crash will be able to figure
* out the call path that was taken. * out the call path that was taken.
*/ */
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
i += printk_address(addr); i += printk_address(addr);
i += printk(" "); i += printk(" ");
if (i > 50) { if (i > 50) {
...@@ -185,7 +185,7 @@ void show_trace(unsigned long *stack) ...@@ -185,7 +185,7 @@ void show_trace(unsigned long *stack)
while (((long) stack & (THREAD_SIZE-1)) != 0) { while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack++; addr = *stack++;
if (kernel_text_address(addr)) { if (__kernel_text_address(addr)) {
i += printk_address(addr); i += printk_address(addr);
i += printk(" "); i += printk(" ");
if (i > 50) { if (i > 50) {
......
...@@ -93,6 +93,7 @@ extern int get_option(char **str, int *pint); ...@@ -93,6 +93,7 @@ extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints); extern char *get_options(const char *str, int nints, int *ints);
extern unsigned long long memparse(char *ptr, char **retptr); 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 kernel_text_address(unsigned long addr);
extern int session_of_pgrp(int pgrp); extern int session_of_pgrp(int pgrp);
......
...@@ -335,8 +335,9 @@ static inline int module_is_live(struct module *mod) ...@@ -335,8 +335,9 @@ static inline int module_is_live(struct module *mod)
return mod->state != MODULE_STATE_GOING; 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);
struct module *__module_text_address(unsigned long addr);
/* Returns module and fills in value, defined and namebuf, or NULL if /* Returns module and fills in value, defined and namebuf, or NULL if
symnum out of range. */ symnum out of range. */
...@@ -462,6 +463,12 @@ static inline struct module *module_text_address(unsigned long addr) ...@@ -462,6 +463,12 @@ static inline struct module *module_text_address(unsigned long addr)
return NULL; 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) */ /* Get/put a kernel symbol (calls should be symmetric) */
#define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); }) #define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); })
#define symbol_put(x) do { } while(0) #define symbol_put(x) do { } while(0)
......
...@@ -40,7 +40,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) ...@@ -40,7 +40,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
return e; return e;
} }
int kernel_text_address(unsigned long addr) static int core_kernel_text(unsigned long addr)
{ {
if (addr >= (unsigned long)_stext && if (addr >= (unsigned long)_stext &&
addr <= (unsigned long)_etext) addr <= (unsigned long)_etext)
...@@ -49,6 +49,19 @@ int kernel_text_address(unsigned long addr) ...@@ -49,6 +49,19 @@ int kernel_text_address(unsigned long addr)
if (addr >= (unsigned long)_sinittext && if (addr >= (unsigned long)_sinittext &&
addr <= (unsigned long)_einittext) addr <= (unsigned long)_einittext)
return 1; 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; return module_text_address(addr) != NULL;
} }
...@@ -2128,7 +2128,7 @@ const struct exception_table_entry *search_module_extables(unsigned long addr) ...@@ -2128,7 +2128,7 @@ const struct exception_table_entry *search_module_extables(unsigned long addr)
} }
/* Is this a valid kernel address? We don't grab the lock: we are oopsing. */ /* 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; struct module *mod;
...@@ -2139,6 +2139,18 @@ struct module *module_text_address(unsigned long addr) ...@@ -2139,6 +2139,18 @@ struct module *module_text_address(unsigned long addr)
return NULL; 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. */ /* Don't grab lock, we're oopsing. */
void print_modules(void) void print_modules(void)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment