Skip to content

Commit 45ac65d

Browse files
mhiramatSasha Levin
authored andcommitted
x86/kprobes: Avoid kretprobe recursion bug
[ Upstream commit b191fa9 ] Avoid kretprobe recursion loop bg by setting a dummy kprobes to current_kprobe per-CPU variable. This bug has been introduced with the asm-coded trampoline code, since previously it used another kprobe for hooking the function return placeholder (which only has a nop) and trampoline handler was called from that kprobe. This revives the old lost kprobe again. With this fix, we don't see deadlock anymore. And you can see that all inner-called kretprobe are skipped. event_1 235 0 event_2 19375 19612 The 1st column is recorded count and the 2nd is missed count. Above shows (event_1 rec) + (event_2 rec) ~= (event_2 missed) (some difference are here because the counter is racy) Reported-by: Andrea Righi <[email protected]> Tested-by: Andrea Righi <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Acked-by: Steven Rostedt <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Fixes: c9becf5 ("[PATCH] kretprobe: kretprobe-booster") Link: http://lkml.kernel.org/r/155094064889.6137.972160690963039.stgit@devbox Signed-off-by: Ingo Molnar <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent a07f99a commit 45ac65d

File tree

1 file changed

+20
-2
lines changed

1 file changed

+20
-2
lines changed

arch/x86/kernel/kprobes/core.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,11 +740,16 @@ asm(
740740
NOKPROBE_SYMBOL(kretprobe_trampoline);
741741
STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
742742

743+
static struct kprobe kretprobe_kprobe = {
744+
.addr = (void *)kretprobe_trampoline,
745+
};
746+
743747
/*
744748
* Called from kretprobe_trampoline
745749
*/
746750
__visible __used void *trampoline_handler(struct pt_regs *regs)
747751
{
752+
struct kprobe_ctlblk *kcb;
748753
struct kretprobe_instance *ri = NULL;
749754
struct hlist_head *head, empty_rp;
750755
struct hlist_node *tmp;
@@ -754,6 +759,17 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
754759
void *frame_pointer;
755760
bool skipped = false;
756761

762+
preempt_disable();
763+
764+
/*
765+
* Set a dummy kprobe for avoiding kretprobe recursion.
766+
* Since kretprobe never run in kprobe handler, kprobe must not
767+
* be running at this point.
768+
*/
769+
kcb = get_kprobe_ctlblk();
770+
__this_cpu_write(current_kprobe, &kretprobe_kprobe);
771+
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
772+
757773
INIT_HLIST_HEAD(&empty_rp);
758774
kretprobe_hash_lock(current, &head, &flags);
759775
/* fixup registers */
@@ -829,10 +845,9 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
829845
orig_ret_address = (unsigned long)ri->ret_addr;
830846
if (ri->rp && ri->rp->handler) {
831847
__this_cpu_write(current_kprobe, &ri->rp->kp);
832-
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
833848
ri->ret_addr = correct_ret_addr;
834849
ri->rp->handler(ri, regs);
835-
__this_cpu_write(current_kprobe, NULL);
850+
__this_cpu_write(current_kprobe, &kretprobe_kprobe);
836851
}
837852

838853
recycle_rp_inst(ri, &empty_rp);
@@ -848,6 +863,9 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
848863

849864
kretprobe_hash_unlock(current, &flags);
850865

866+
__this_cpu_write(current_kprobe, NULL);
867+
preempt_enable();
868+
851869
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
852870
hlist_del(&ri->hlist);
853871
kfree(ri);

0 commit comments

Comments
 (0)