Skip to content

Commit 1703abb

Browse files
t-8chmhiramat
authored andcommitted
kprobes: Reduce preempt disable scope in check_kprobe_access_safe()
Commit a189d03 ("kprobes: disable preempt for module_text_address() and kernel_text_address()") introduced a preempt_disable() region to protect against concurrent module unloading. However this region also includes the call to jump_label_text_reserved() which takes a long time; up to 400us, iterating over approx 6000 jump tables. The scope protected by preempt_disable() is largen than necessary. core_kernel_text() does not need to be protected as it does not interact with module code at all. Only the scope from __module_text_address() to try_module_get() needs to be protected. By limiting the critical section to __module_text_address() and try_module_get() the function responsible for the latency spike remains preemptible. This works fine even when !CONFIG_MODULES as in that case try_module_get() will always return true and that block can be optimized away. Limit the critical section to __module_text_address() and try_module_get(). Use guard(preempt)() for easier error handling. While at it also remove a spurious *probed_mod = NULL in an error path. On errors the output parameter is never inspected by the caller. Some error paths were clearing the parameters, some didn't. Align them for clarity. Link: https://lore.kernel.org/all/[email protected]/ Signed-off-by: Thomas Weißschuh <[email protected]> Reviewed-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent 30c8fd3 commit 1703abb

File tree

1 file changed

+12
-12
lines changed

1 file changed

+12
-12
lines changed

kernel/kprobes.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <linux/static_call.h>
4040
#include <linux/perf_event.h>
4141
#include <linux/execmem.h>
42+
#include <linux/cleanup.h>
4243

4344
#include <asm/sections.h>
4445
#include <asm/cacheflush.h>
@@ -1566,16 +1567,25 @@ static int check_kprobe_address_safe(struct kprobe *p,
15661567
if (ret)
15671568
return ret;
15681569
jump_label_lock();
1569-
preempt_disable();
15701570

15711571
/* Ensure the address is in a text area, and find a module if exists. */
15721572
*probed_mod = NULL;
15731573
if (!core_kernel_text((unsigned long) p->addr)) {
1574+
guard(preempt)();
15741575
*probed_mod = __module_text_address((unsigned long) p->addr);
15751576
if (!(*probed_mod)) {
15761577
ret = -EINVAL;
15771578
goto out;
15781579
}
1580+
1581+
/*
1582+
* We must hold a refcount of the probed module while updating
1583+
* its code to prohibit unexpected unloading.
1584+
*/
1585+
if (unlikely(!try_module_get(*probed_mod))) {
1586+
ret = -ENOENT;
1587+
goto out;
1588+
}
15791589
}
15801590
/* Ensure it is not in reserved area. */
15811591
if (in_gate_area_no_mm((unsigned long) p->addr) ||
@@ -1584,35 +1594,25 @@ static int check_kprobe_address_safe(struct kprobe *p,
15841594
static_call_text_reserved(p->addr, p->addr) ||
15851595
find_bug((unsigned long)p->addr) ||
15861596
is_cfi_preamble_symbol((unsigned long)p->addr)) {
1597+
module_put(*probed_mod);
15871598
ret = -EINVAL;
15881599
goto out;
15891600
}
15901601

15911602
/* Get module refcount and reject __init functions for loaded modules. */
15921603
if (IS_ENABLED(CONFIG_MODULES) && *probed_mod) {
1593-
/*
1594-
* We must hold a refcount of the probed module while updating
1595-
* its code to prohibit unexpected unloading.
1596-
*/
1597-
if (unlikely(!try_module_get(*probed_mod))) {
1598-
ret = -ENOENT;
1599-
goto out;
1600-
}
1601-
16021604
/*
16031605
* If the module freed '.init.text', we couldn't insert
16041606
* kprobes in there.
16051607
*/
16061608
if (within_module_init((unsigned long)p->addr, *probed_mod) &&
16071609
!module_is_coming(*probed_mod)) {
16081610
module_put(*probed_mod);
1609-
*probed_mod = NULL;
16101611
ret = -ENOENT;
16111612
}
16121613
}
16131614

16141615
out:
1615-
preempt_enable();
16161616
jump_label_unlock();
16171617

16181618
return ret;

0 commit comments

Comments
 (0)