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: Zhou Chengming <zhouchengming1@huawei.com> Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org> Acked-by: Steven 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: 2cfa197 "ftrace/alternatives: Introducing *_text_reserved functions" Link: http://lkml.kernel.org/r/1509585501-79466-1-git-send-email-zhouchengming1@huawei.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
d786f05175
commit
e846d13958
@ -442,7 +442,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end,
|
||||
{
|
||||
const s32 *poff;
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
for (poff = start; poff < end; poff++) {
|
||||
u8 *ptr = (u8 *)poff + *poff;
|
||||
|
||||
@ -452,7 +451,6 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end,
|
||||
if (*ptr == 0x3e)
|
||||
text_poke(ptr, ((unsigned char []){0xf0}), 1);
|
||||
}
|
||||
mutex_unlock(&text_mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
for (poff = start; poff < end; poff++) {
|
||||
u8 *ptr = (u8 *)poff + *poff;
|
||||
|
||||
@ -470,7 +467,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end,
|
||||
if (*ptr == 0xf0)
|
||||
text_poke(ptr, ((unsigned char []){0x3E}), 1);
|
||||
}
|
||||
mutex_unlock(&text_mutex);
|
||||
}
|
||||
|
||||
struct smp_alt_module {
|
||||
@ -489,8 +485,7 @@ struct smp_alt_module {
|
||||
struct list_head next;
|
||||
};
|
||||
static LIST_HEAD(smp_alt_modules);
|
||||
static DEFINE_MUTEX(smp_alt);
|
||||
static bool uniproc_patched = false; /* protected by smp_alt */
|
||||
static bool uniproc_patched = false; /* protected by text_mutex */
|
||||
|
||||
void __init_or_module alternatives_smp_module_add(struct module *mod,
|
||||
char *name,
|
||||
@ -499,7 +494,7 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
|
||||
{
|
||||
struct smp_alt_module *smp;
|
||||
|
||||
mutex_lock(&smp_alt);
|
||||
mutex_lock(&text_mutex);
|
||||
if (!uniproc_patched)
|
||||
goto unlock;
|
||||
|
||||
@ -526,14 +521,14 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
|
||||
smp_unlock:
|
||||
alternatives_smp_unlock(locks, locks_end, text, text_end);
|
||||
unlock:
|
||||
mutex_unlock(&smp_alt);
|
||||
mutex_unlock(&text_mutex);
|
||||
}
|
||||
|
||||
void __init_or_module alternatives_smp_module_del(struct module *mod)
|
||||
{
|
||||
struct smp_alt_module *item;
|
||||
|
||||
mutex_lock(&smp_alt);
|
||||
mutex_lock(&text_mutex);
|
||||
list_for_each_entry(item, &smp_alt_modules, next) {
|
||||
if (mod != item->mod)
|
||||
continue;
|
||||
@ -541,7 +536,7 @@ void __init_or_module alternatives_smp_module_del(struct module *mod)
|
||||
kfree(item);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&smp_alt);
|
||||
mutex_unlock(&text_mutex);
|
||||
}
|
||||
|
||||
void alternatives_enable_smp(void)
|
||||
@ -551,7 +546,7 @@ void alternatives_enable_smp(void)
|
||||
/* Why bother if there are no other CPUs? */
|
||||
BUG_ON(num_possible_cpus() == 1);
|
||||
|
||||
mutex_lock(&smp_alt);
|
||||
mutex_lock(&text_mutex);
|
||||
|
||||
if (uniproc_patched) {
|
||||
pr_info("switching to SMP code\n");
|
||||
@ -563,10 +558,13 @@ void alternatives_enable_smp(void)
|
||||
mod->text, mod->text_end);
|
||||
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)
|
||||
{
|
||||
struct smp_alt_module *mod;
|
||||
@ -574,6 +572,8 @@ int alternatives_text_reserved(void *start, void *end)
|
||||
u8 *text_start = start;
|
||||
u8 *text_end = end;
|
||||
|
||||
lockdep_assert_held(&text_mutex);
|
||||
|
||||
list_for_each_entry(mod, &smp_alt_modules, next) {
|
||||
if (mod->text > text_end || mod->text_end < text_start)
|
||||
continue;
|
||||
|
@ -31,6 +31,8 @@
|
||||
* mutex protecting text section modification (dynamic code patching).
|
||||
* 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.
|
||||
*/
|
||||
DEFINE_MUTEX(text_mutex);
|
||||
|
Loading…
x
Reference in New Issue
Block a user