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); |
