272 lines
8.2 KiB
Diff
272 lines
8.2 KiB
Diff
From 9aad2d11106f9a42593ee8eb3f10bc469873a18e Mon Sep 17 00:00:00 2001
|
|
From: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Date: Tue, 5 Oct 2010 11:29:27 -0700
|
|
Subject: [PATCH] modules: Fix module_bug_list list corruption race
|
|
|
|
With all the recent module loading cleanups, we've minimized the code
|
|
that sits under module_mutex, fixing various deadlocks and making it
|
|
possible to do most of the module loading in parallel.
|
|
|
|
However, that whole conversion totally missed the rather obscure code
|
|
that adds a new module to the list for BUG() handling. That code was
|
|
doubly obscure because (a) the code itself lives in lib/bugs.c (for
|
|
dubious reasons) and (b) it gets called from the architecture-specific
|
|
"module_finalize()" rather than from generic code.
|
|
|
|
Calling it from arch-specific code makes no sense what-so-ever to begin
|
|
with, and is now actively wrong since that code isn't protected by the
|
|
module loading lock any more.
|
|
|
|
So this commit moves the "module_bug_{finalize,cleanup}()" calls away
|
|
from the arch-specific code, and into the generic code - and in the
|
|
process protects it with the module_mutex so that the list operations
|
|
are now safe.
|
|
|
|
Future fixups:
|
|
- move the module list handling code into kernel/module.c where it
|
|
belongs.
|
|
- get rid of 'module_bug_list' and just use the regular list of modules
|
|
(called 'modules' - imagine that) that we already create and maintain
|
|
for other reasons.
|
|
|
|
Reported-and-tested-by: Thomas Gleixner <tglx@linutronix.de>
|
|
Cc: Rusty Russell <rusty@rustcorp.com.au>
|
|
Cc: Adrian Bunk <bunk@kernel.org>
|
|
Cc: Andrew Morton <akpm@linux-foundation.org>
|
|
Cc: stable@kernel.org
|
|
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
---
|
|
arch/avr32/kernel/module.c | 3 +--
|
|
arch/h8300/kernel/module.c | 3 +--
|
|
arch/mn10300/kernel/module.c | 3 +--
|
|
arch/parisc/kernel/module.c | 3 +--
|
|
arch/powerpc/kernel/module.c | 5 -----
|
|
arch/s390/kernel/module.c | 3 +--
|
|
arch/sh/kernel/module.c | 2 --
|
|
arch/x86/kernel/module.c | 3 +--
|
|
include/linux/module.h | 5 ++---
|
|
kernel/module.c | 4 ++++
|
|
lib/bug.c | 6 ++----
|
|
11 files changed, 14 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c
|
|
index 98f94d0..a727f54 100644
|
|
--- a/arch/avr32/kernel/module.c
|
|
+++ b/arch/avr32/kernel/module.c
|
|
@@ -314,10 +314,9 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
|
vfree(module->arch.syminfo);
|
|
module->arch.syminfo = NULL;
|
|
|
|
- return module_bug_finalize(hdr, sechdrs, module);
|
|
+ return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *module)
|
|
{
|
|
- module_bug_cleanup(module);
|
|
}
|
|
diff --git a/arch/h8300/kernel/module.c b/arch/h8300/kernel/module.c
|
|
index 0865e29..db4953d 100644
|
|
--- a/arch/h8300/kernel/module.c
|
|
+++ b/arch/h8300/kernel/module.c
|
|
@@ -112,10 +112,9 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
const Elf_Shdr *sechdrs,
|
|
struct module *me)
|
|
{
|
|
- return module_bug_finalize(hdr, sechdrs, me);
|
|
+ return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c
|
|
index 6aea7fd..196a111 100644
|
|
--- a/arch/mn10300/kernel/module.c
|
|
+++ b/arch/mn10300/kernel/module.c
|
|
@@ -206,7 +206,7 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
const Elf_Shdr *sechdrs,
|
|
struct module *me)
|
|
{
|
|
- return module_bug_finalize(hdr, sechdrs, me);
|
|
+ return 0;
|
|
}
|
|
|
|
/*
|
|
@@ -214,5 +214,4 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
*/
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
|
|
index 159a2b8..6e81bb5 100644
|
|
--- a/arch/parisc/kernel/module.c
|
|
+++ b/arch/parisc/kernel/module.c
|
|
@@ -941,11 +941,10 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
nsyms = newptr - (Elf_Sym *)symhdr->sh_addr;
|
|
DEBUGP("NEW num_symtab %lu\n", nsyms);
|
|
symhdr->sh_size = nsyms * sizeof(Elf_Sym);
|
|
- return module_bug_finalize(hdr, sechdrs, me);
|
|
+ return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
deregister_unwind_table(mod);
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c
|
|
index 477c663..4ef93ae 100644
|
|
--- a/arch/powerpc/kernel/module.c
|
|
+++ b/arch/powerpc/kernel/module.c
|
|
@@ -65,10 +65,6 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
const Elf_Shdr *sect;
|
|
int err;
|
|
|
|
- err = module_bug_finalize(hdr, sechdrs, me);
|
|
- if (err)
|
|
- return err;
|
|
-
|
|
/* Apply feature fixups */
|
|
sect = find_section(hdr, sechdrs, "__ftr_fixup");
|
|
if (sect != NULL)
|
|
@@ -101,5 +97,4 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
|
|
index 22cfd63..f7167ee 100644
|
|
--- a/arch/s390/kernel/module.c
|
|
+++ b/arch/s390/kernel/module.c
|
|
@@ -407,10 +407,9 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
{
|
|
vfree(me->arch.syminfo);
|
|
me->arch.syminfo = NULL;
|
|
- return module_bug_finalize(hdr, sechdrs, me);
|
|
+ return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/arch/sh/kernel/module.c b/arch/sh/kernel/module.c
|
|
index 43adddf..ae0be69 100644
|
|
--- a/arch/sh/kernel/module.c
|
|
+++ b/arch/sh/kernel/module.c
|
|
@@ -149,13 +149,11 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
int ret = 0;
|
|
|
|
ret |= module_dwarf_finalize(hdr, sechdrs, me);
|
|
- ret |= module_bug_finalize(hdr, sechdrs, me);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
- module_bug_cleanup(mod);
|
|
module_dwarf_cleanup(mod);
|
|
}
|
|
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
|
|
index e0bc186..1c355c5 100644
|
|
--- a/arch/x86/kernel/module.c
|
|
+++ b/arch/x86/kernel/module.c
|
|
@@ -239,11 +239,10 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|
apply_paravirt(pseg, pseg + para->sh_size);
|
|
}
|
|
|
|
- return module_bug_finalize(hdr, sechdrs, me);
|
|
+ return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
alternatives_smp_module_del(mod);
|
|
- module_bug_cleanup(mod);
|
|
}
|
|
diff --git a/include/linux/module.h b/include/linux/module.h
|
|
index 8a6b9fd..aace066 100644
|
|
--- a/include/linux/module.h
|
|
+++ b/include/linux/module.h
|
|
@@ -686,17 +686,16 @@ extern int module_sysfs_initialized;
|
|
|
|
|
|
#ifdef CONFIG_GENERIC_BUG
|
|
-int module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,
|
|
+void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,
|
|
struct module *);
|
|
void module_bug_cleanup(struct module *);
|
|
|
|
#else /* !CONFIG_GENERIC_BUG */
|
|
|
|
-static inline int module_bug_finalize(const Elf_Ehdr *hdr,
|
|
+static inline void module_bug_finalize(const Elf_Ehdr *hdr,
|
|
const Elf_Shdr *sechdrs,
|
|
struct module *mod)
|
|
{
|
|
- return 0;
|
|
}
|
|
static inline void module_bug_cleanup(struct module *mod) {}
|
|
#endif /* CONFIG_GENERIC_BUG */
|
|
diff --git a/kernel/module.c b/kernel/module.c
|
|
index 6c56282..eca6e02 100644
|
|
--- a/kernel/module.c
|
|
+++ b/kernel/module.c
|
|
@@ -1533,6 +1533,7 @@ static int __unlink_module(void *_mod)
|
|
{
|
|
struct module *mod = _mod;
|
|
list_del(&mod->list);
|
|
+ module_bug_cleanup(mod);
|
|
return 0;
|
|
}
|
|
|
|
@@ -2538,6 +2539,7 @@ static noinline struct module *load_module(void __user *umod,
|
|
if (err < 0)
|
|
goto ddebug;
|
|
|
|
+ module_bug_finalize(hdr, sechdrs, mod);
|
|
list_add_rcu(&mod->list, &modules);
|
|
mutex_unlock(&module_mutex);
|
|
|
|
@@ -2564,6 +2566,8 @@ static noinline struct module *load_module(void __user *umod,
|
|
mutex_lock(&module_mutex);
|
|
/* Unlink carefully: kallsyms could be walking list. */
|
|
list_del_rcu(&mod->list);
|
|
+ module_bug_cleanup(mod);
|
|
+
|
|
ddebug:
|
|
dynamic_debug_remove(debug);
|
|
unlock:
|
|
diff --git a/lib/bug.c b/lib/bug.c
|
|
index f13daf4..c005615 100644
|
|
--- a/lib/bug.c
|
|
+++ b/lib/bug.c
|
|
@@ -72,8 +72,8 @@ static const struct bug_entry *module_find_bug(unsigned long bugaddr)
|
|
return NULL;
|
|
}
|
|
|
|
-int module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
|
- struct module *mod)
|
|
+void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
|
+ struct module *mod)
|
|
{
|
|
char *secstrings;
|
|
unsigned int i;
|
|
@@ -97,8 +97,6 @@ int module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
|
* could potentially lead to deadlock and thus be counter-productive.
|
|
*/
|
|
list_add(&mod->bug_list, &module_bug_list);
|
|
-
|
|
- return 0;
|
|
}
|
|
|
|
void module_bug_cleanup(struct module *mod)
|
|
--
|
|
1.7.6
|
|
|