diff options
author | Masami Hiramatsu <mhiramat@kernel.org> | 2019-02-23 11:50:49 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2019-04-19 08:26:07 -0400 |
commit | b191fa96ea6dc00d331dcc28c1f7db5e075693a0 (patch) | |
tree | 2d4eaf3bd9fde019704de4a90b6bca19195a3de5 | |
parent | fabe38ab6b2bd9418350284c63825f13b8a6abba (diff) |
x86/kprobes: Avoid kretprobe recursion bug
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 <righi.andrea@gmail.com>
Tested-by: Andrea Righi <righi.andrea@gmail.com>
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Fixes: c9becf58d935 ("[PATCH] kretprobe: kretprobe-booster")
Link: http://lkml.kernel.org/r/155094064889.6137.972160690963039.stgit@devbox
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/kernel/kprobes/core.c | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 18fbe9be2d68..fed46ddb1eef 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c | |||
@@ -749,11 +749,16 @@ asm( | |||
749 | NOKPROBE_SYMBOL(kretprobe_trampoline); | 749 | NOKPROBE_SYMBOL(kretprobe_trampoline); |
750 | STACK_FRAME_NON_STANDARD(kretprobe_trampoline); | 750 | STACK_FRAME_NON_STANDARD(kretprobe_trampoline); |
751 | 751 | ||
752 | static struct kprobe kretprobe_kprobe = { | ||
753 | .addr = (void *)kretprobe_trampoline, | ||
754 | }; | ||
755 | |||
752 | /* | 756 | /* |
753 | * Called from kretprobe_trampoline | 757 | * Called from kretprobe_trampoline |
754 | */ | 758 | */ |
755 | static __used void *trampoline_handler(struct pt_regs *regs) | 759 | static __used void *trampoline_handler(struct pt_regs *regs) |
756 | { | 760 | { |
761 | struct kprobe_ctlblk *kcb; | ||
757 | struct kretprobe_instance *ri = NULL; | 762 | struct kretprobe_instance *ri = NULL; |
758 | struct hlist_head *head, empty_rp; | 763 | struct hlist_head *head, empty_rp; |
759 | struct hlist_node *tmp; | 764 | struct hlist_node *tmp; |
@@ -763,6 +768,17 @@ static __used void *trampoline_handler(struct pt_regs *regs) | |||
763 | void *frame_pointer; | 768 | void *frame_pointer; |
764 | bool skipped = false; | 769 | bool skipped = false; |
765 | 770 | ||
771 | preempt_disable(); | ||
772 | |||
773 | /* | ||
774 | * Set a dummy kprobe for avoiding kretprobe recursion. | ||
775 | * Since kretprobe never run in kprobe handler, kprobe must not | ||
776 | * be running at this point. | ||
777 | */ | ||
778 | kcb = get_kprobe_ctlblk(); | ||
779 | __this_cpu_write(current_kprobe, &kretprobe_kprobe); | ||
780 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
781 | |||
766 | INIT_HLIST_HEAD(&empty_rp); | 782 | INIT_HLIST_HEAD(&empty_rp); |
767 | kretprobe_hash_lock(current, &head, &flags); | 783 | kretprobe_hash_lock(current, &head, &flags); |
768 | /* fixup registers */ | 784 | /* fixup registers */ |
@@ -838,10 +854,9 @@ static __used void *trampoline_handler(struct pt_regs *regs) | |||
838 | orig_ret_address = (unsigned long)ri->ret_addr; | 854 | orig_ret_address = (unsigned long)ri->ret_addr; |
839 | if (ri->rp && ri->rp->handler) { | 855 | if (ri->rp && ri->rp->handler) { |
840 | __this_cpu_write(current_kprobe, &ri->rp->kp); | 856 | __this_cpu_write(current_kprobe, &ri->rp->kp); |
841 | get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; | ||
842 | ri->ret_addr = correct_ret_addr; | 857 | ri->ret_addr = correct_ret_addr; |
843 | ri->rp->handler(ri, regs); | 858 | ri->rp->handler(ri, regs); |
844 | __this_cpu_write(current_kprobe, NULL); | 859 | __this_cpu_write(current_kprobe, &kretprobe_kprobe); |
845 | } | 860 | } |
846 | 861 | ||
847 | recycle_rp_inst(ri, &empty_rp); | 862 | recycle_rp_inst(ri, &empty_rp); |
@@ -857,6 +872,9 @@ static __used void *trampoline_handler(struct pt_regs *regs) | |||
857 | 872 | ||
858 | kretprobe_hash_unlock(current, &flags); | 873 | kretprobe_hash_unlock(current, &flags); |
859 | 874 | ||
875 | __this_cpu_write(current_kprobe, NULL); | ||
876 | preempt_enable(); | ||
877 | |||
860 | hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { | 878 | hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { |
861 | hlist_del(&ri->hlist); | 879 | hlist_del(&ri->hlist); |
862 | kfree(ri); | 880 | kfree(ri); |