Commit e846d139 authored by Zhou Chengming's avatar Zhou Chengming Committed by Ingo Molnar

kprobes, x86/alternatives: Use text_mutex to protect smp_alt_modules

We use alternatives_text_reserved() to check if the address is in
the fixed pieces of alternative reserved, but the problem is that
we don't hold the smp_alt mutex when call this function. So the list
traversal may encounter a deleted list_head if another path is doing
alternatives_smp_module_del().

One solution is that we can hold smp_alt mutex before call this
function, but the difficult point is that the callers of this
functions, arch_prepare_kprobe() and arch_prepare_optimized_kprobe(),
are called inside the text_mutex. So we must hold smp_alt mutex
before we go into these arch dependent code. But we can't now,
the smp_alt mutex is the arch dependent part, only x86 has it.
Maybe we can export another arch dependent callback to solve this.

But there is a simpler way to handle this problem. We can reuse the
text_mutex to protect smp_alt_modules instead of using another mutex.
And all the arch dependent checks of kprobes are inside the text_mutex,
so it's safe now.
Signed-off-by: default avatarZhou Chengming <zhouchengming1@huawei.com>
Reviewed-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Acked-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Fixes: 2cfa1978 "ftrace/alternatives: Introducing *_text_reserved functions"
Link: http://lkml.kernel.org/r/1509585501-79466-1-git-send-email-zhouchengming1@huawei.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent d786f051
...@@ -442,7 +442,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end, ...@@ -442,7 +442,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end,
{ {
const s32 *poff; const s32 *poff;
mutex_lock(&text_mutex);
for (poff = start; poff < end; poff++) { for (poff = start; poff < end; poff++) {
u8 *ptr = (u8 *)poff + *poff; u8 *ptr = (u8 *)poff + *poff;
...@@ -452,7 +451,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end, ...@@ -452,7 +451,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end,
if (*ptr == 0x3e) if (*ptr == 0x3e)
text_poke(ptr, ((unsigned char []){0xf0}), 1); text_poke(ptr, ((unsigned char []){0xf0}), 1);
} }
mutex_unlock(&text_mutex);
} }
static void alternatives_smp_unlock(const s32 *start, const s32 *end, static void alternatives_smp_unlock(const s32 *start, const s32 *end,
...@@ -460,7 +458,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end, ...@@ -460,7 +458,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end,
{ {
const s32 *poff; const s32 *poff;
mutex_lock(&text_mutex);
for (poff = start; poff < end; poff++) { for (poff = start; poff < end; poff++) {
u8 *ptr = (u8 *)poff + *poff; u8 *ptr = (u8 *)poff + *poff;
...@@ -470,7 +467,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end, ...@@ -470,7 +467,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end,
if (*ptr == 0xf0) if (*ptr == 0xf0)
text_poke(ptr, ((unsigned char []){0x3E}), 1); text_poke(ptr, ((unsigned char []){0x3E}), 1);
} }
mutex_unlock(&text_mutex);
} }
struct smp_alt_module { struct smp_alt_module {
...@@ -489,8 +485,7 @@ struct smp_alt_module { ...@@ -489,8 +485,7 @@ struct smp_alt_module {
struct list_head next; struct list_head next;
}; };
static LIST_HEAD(smp_alt_modules); static LIST_HEAD(smp_alt_modules);
static DEFINE_MUTEX(smp_alt); static bool uniproc_patched = false; /* protected by text_mutex */
static bool uniproc_patched = false; /* protected by smp_alt */
void __init_or_module alternatives_smp_module_add(struct module *mod, void __init_or_module alternatives_smp_module_add(struct module *mod,
char *name, char *name,
...@@ -499,7 +494,7 @@ void __init_or_module alternatives_smp_module_add(struct module *mod, ...@@ -499,7 +494,7 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
{ {
struct smp_alt_module *smp; struct smp_alt_module *smp;
mutex_lock(&smp_alt); mutex_lock(&text_mutex);
if (!uniproc_patched) if (!uniproc_patched)
goto unlock; goto unlock;
...@@ -526,14 +521,14 @@ void __init_or_module alternatives_smp_module_add(struct module *mod, ...@@ -526,14 +521,14 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
smp_unlock: smp_unlock:
alternatives_smp_unlock(locks, locks_end, text, text_end); alternatives_smp_unlock(locks, locks_end, text, text_end);
unlock: unlock:
mutex_unlock(&smp_alt); mutex_unlock(&text_mutex);
} }
void __init_or_module alternatives_smp_module_del(struct module *mod) void __init_or_module alternatives_smp_module_del(struct module *mod)
{ {
struct smp_alt_module *item; struct smp_alt_module *item;
mutex_lock(&smp_alt); mutex_lock(&text_mutex);
list_for_each_entry(item, &smp_alt_modules, next) { list_for_each_entry(item, &smp_alt_modules, next) {
if (mod != item->mod) if (mod != item->mod)
continue; continue;
...@@ -541,7 +536,7 @@ void __init_or_module alternatives_smp_module_del(struct module *mod) ...@@ -541,7 +536,7 @@ void __init_or_module alternatives_smp_module_del(struct module *mod)
kfree(item); kfree(item);
break; break;
} }
mutex_unlock(&smp_alt); mutex_unlock(&text_mutex);
} }
void alternatives_enable_smp(void) void alternatives_enable_smp(void)
...@@ -551,7 +546,7 @@ void alternatives_enable_smp(void) ...@@ -551,7 +546,7 @@ void alternatives_enable_smp(void)
/* Why bother if there are no other CPUs? */ /* Why bother if there are no other CPUs? */
BUG_ON(num_possible_cpus() == 1); BUG_ON(num_possible_cpus() == 1);
mutex_lock(&smp_alt); mutex_lock(&text_mutex);
if (uniproc_patched) { if (uniproc_patched) {
pr_info("switching to SMP code\n"); pr_info("switching to SMP code\n");
...@@ -563,10 +558,13 @@ void alternatives_enable_smp(void) ...@@ -563,10 +558,13 @@ void alternatives_enable_smp(void)
mod->text, mod->text_end); mod->text, mod->text_end);
uniproc_patched = false; uniproc_patched = false;
} }
mutex_unlock(&smp_alt); mutex_unlock(&text_mutex);
} }
/* Return 1 if the address range is reserved for smp-alternatives */ /*
* Return 1 if the address range is reserved for SMP-alternatives.
* Must hold text_mutex.
*/
int alternatives_text_reserved(void *start, void *end) int alternatives_text_reserved(void *start, void *end)
{ {
struct smp_alt_module *mod; struct smp_alt_module *mod;
...@@ -574,6 +572,8 @@ int alternatives_text_reserved(void *start, void *end) ...@@ -574,6 +572,8 @@ int alternatives_text_reserved(void *start, void *end)
u8 *text_start = start; u8 *text_start = start;
u8 *text_end = end; u8 *text_end = end;
lockdep_assert_held(&text_mutex);
list_for_each_entry(mod, &smp_alt_modules, next) { list_for_each_entry(mod, &smp_alt_modules, next) {
if (mod->text > text_end || mod->text_end < text_start) if (mod->text > text_end || mod->text_end < text_start)
continue; continue;
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
* mutex protecting text section modification (dynamic code patching). * mutex protecting text section modification (dynamic code patching).
* some users need to sleep (allocating memory...) while they hold this lock. * some users need to sleep (allocating memory...) while they hold this lock.
* *
* Note: Also protects SMP-alternatives modification on x86.
*
* NOT exported to modules - patching kernel text is a really delicate matter. * NOT exported to modules - patching kernel text is a really delicate matter.
*/ */
DEFINE_MUTEX(text_mutex); DEFINE_MUTEX(text_mutex);
......
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