| From dcfc47248d3f7d28df6f531e6426b933de94370d Mon Sep 17 00:00:00 2001 |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| Date: Sat, 11 Jun 2016 23:06:53 +0900 |
| Subject: kprobes/x86: Clear TF bit in fault on single-stepping |
| |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| |
| commit dcfc47248d3f7d28df6f531e6426b933de94370d upstream. |
| |
| Fix kprobe_fault_handler() to clear the TF (trap flag) bit of |
| the flags register in the case of a fault fixup on single-stepping. |
| |
| If we put a kprobe on the instruction which caused a |
| page fault (e.g. actual mov instructions in copy_user_*), |
| that fault happens on the single-stepping buffer. In this |
| case, kprobes resets running instance so that the CPU can |
| retry execution on the original ip address. |
| |
| However, current code forgets to reset the TF bit. Since this |
| fault happens with TF bit set for enabling single-stepping, |
| when it retries, it causes a debug exception and kprobes |
| can not handle it because it already reset itself. |
| |
| On the most of x86-64 platform, it can be easily reproduced |
| by using kprobe tracer. E.g. |
| |
| # cd /sys/kernel/debug/tracing |
| # echo p copy_user_enhanced_fast_string+5 > kprobe_events |
| # echo 1 > events/kprobes/enable |
| |
| And you'll see a kernel panic on do_debug(), since the debug |
| trap is not handled by kprobes. |
| |
| To fix this problem, we just need to clear the TF bit when |
| resetting running kprobe. |
| |
| Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> |
| Reviewed-by: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> |
| Acked-by: Steven Rostedt <rostedt@goodmis.org> |
| Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> |
| Cc: Andy Lutomirski <luto@kernel.org> |
| Cc: Arnaldo Carvalho de Melo <acme@redhat.com> |
| Cc: Borislav Petkov <bp@alien8.de> |
| Cc: Brian Gerst <brgerst@gmail.com> |
| Cc: Denys Vlasenko <dvlasenk@redhat.com> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Jiri Olsa <jolsa@redhat.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Stephane Eranian <eranian@google.com> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Vince Weaver <vincent.weaver@maine.edu> |
| Cc: systemtap@sourceware.org |
| Link: http://lkml.kernel.org/r/20160611140648.25885.37482.stgit@devbox |
| [ Updated the comments. ] |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/kprobes/core.c | 12 ++++++++++++ |
| 1 file changed, 12 insertions(+) |
| |
| --- a/arch/x86/kernel/kprobes/core.c |
| +++ b/arch/x86/kernel/kprobes/core.c |
| @@ -959,7 +959,19 @@ int kprobe_fault_handler(struct pt_regs |
| * normal page fault. |
| */ |
| regs->ip = (unsigned long)cur->addr; |
| + /* |
| + * Trap flag (TF) has been set here because this fault |
| + * happened where the single stepping will be done. |
| + * So clear it by resetting the current kprobe: |
| + */ |
| + regs->flags &= ~X86_EFLAGS_TF; |
| + |
| + /* |
| + * If the TF flag was set before the kprobe hit, |
| + * don't touch it: |
| + */ |
| regs->flags |= kcb->kprobe_old_flags; |
| + |
| if (kcb->kprobe_status == KPROBE_REENTER) |
| restore_previous_kprobe(kcb); |
| else |