| From foo@baz Wed Dec 6 18:04:41 CET 2017 |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| Date: Tue, 19 Sep 2017 19:01:40 +0900 |
| Subject: kprobes/x86: Disable preemption in ftrace-based jprobes |
| |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| |
| |
| [ Upstream commit 5bb4fc2d8641219732eb2bb654206775a4219aca ] |
| |
| Disable preemption in ftrace-based jprobe handlers as |
| described in Documentation/kprobes.txt: |
| |
| "Probe handlers are run with preemption disabled." |
| |
| This will fix jprobes behavior when CONFIG_PREEMPT=y. |
| |
| Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> |
| Cc: Alexei Starovoitov <ast@fb.com> |
| Cc: Alexei Starovoitov <ast@kernel.org> |
| Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Paul E . McKenney <paulmck@linux.vnet.ibm.com> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Steven Rostedt <rostedt@goodmis.org> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Link: http://lkml.kernel.org/r/150581530024.32348.9863783558598926771.stgit@devbox |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/kernel/kprobes/ftrace.c | 23 ++++++++++++++--------- |
| 1 file changed, 14 insertions(+), 9 deletions(-) |
| |
| --- a/arch/x86/kernel/kprobes/ftrace.c |
| +++ b/arch/x86/kernel/kprobes/ftrace.c |
| @@ -26,7 +26,7 @@ |
| #include "common.h" |
| |
| static nokprobe_inline |
| -int __skip_singlestep(struct kprobe *p, struct pt_regs *regs, |
| +void __skip_singlestep(struct kprobe *p, struct pt_regs *regs, |
| struct kprobe_ctlblk *kcb, unsigned long orig_ip) |
| { |
| /* |
| @@ -41,20 +41,21 @@ int __skip_singlestep(struct kprobe *p, |
| __this_cpu_write(current_kprobe, NULL); |
| if (orig_ip) |
| regs->ip = orig_ip; |
| - return 1; |
| } |
| |
| int skip_singlestep(struct kprobe *p, struct pt_regs *regs, |
| struct kprobe_ctlblk *kcb) |
| { |
| - if (kprobe_ftrace(p)) |
| - return __skip_singlestep(p, regs, kcb, 0); |
| - else |
| - return 0; |
| + if (kprobe_ftrace(p)) { |
| + __skip_singlestep(p, regs, kcb, 0); |
| + preempt_enable_no_resched(); |
| + return 1; |
| + } |
| + return 0; |
| } |
| NOKPROBE_SYMBOL(skip_singlestep); |
| |
| -/* Ftrace callback handler for kprobes */ |
| +/* Ftrace callback handler for kprobes -- called under preepmt disabed */ |
| void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, |
| struct ftrace_ops *ops, struct pt_regs *regs) |
| { |
| @@ -77,13 +78,17 @@ void kprobe_ftrace_handler(unsigned long |
| /* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */ |
| regs->ip = ip + sizeof(kprobe_opcode_t); |
| |
| + /* To emulate trap based kprobes, preempt_disable here */ |
| + preempt_disable(); |
| __this_cpu_write(current_kprobe, p); |
| kcb->kprobe_status = KPROBE_HIT_ACTIVE; |
| - if (!p->pre_handler || !p->pre_handler(p, regs)) |
| + if (!p->pre_handler || !p->pre_handler(p, regs)) { |
| __skip_singlestep(p, regs, kcb, orig_ip); |
| + preempt_enable_no_resched(); |
| + } |
| /* |
| * If pre_handler returns !0, it sets regs->ip and |
| - * resets current kprobe. |
| + * resets current kprobe, and keep preempt count +1. |
| */ |
| } |
| end: |