aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@kernel.org>2019-02-23 11:50:49 -0500
committerIngo Molnar <mingo@kernel.org>2019-04-19 08:26:07 -0400
commitb191fa96ea6dc00d331dcc28c1f7db5e075693a0 (patch)
tree2d4eaf3bd9fde019704de4a90b6bca19195a3de5
parentfabe38ab6b2bd9418350284c63825f13b8a6abba (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.c22
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(
749NOKPROBE_SYMBOL(kretprobe_trampoline); 749NOKPROBE_SYMBOL(kretprobe_trampoline);
750STACK_FRAME_NON_STANDARD(kretprobe_trampoline); 750STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
751 751
752static 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 */
755static __used void *trampoline_handler(struct pt_regs *regs) 759static __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);