lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <148821206816.15765.7737579843541819311.stgit@devbox>
Date:   Tue, 28 Feb 2017 01:14:38 +0900
From:   Masami Hiramatsu <mhiramat@...nel.org>
To:     Peter Zijlstra <peterz@...radead.org>
Cc:     Masami Hiramatsu <mhiramat@...nel.org>,
        Borislav Petkov <bp@...en8.de>, linux-kernel@...r.kernel.org,
        Ingo Molnar <mingo@...nel.org>,
        Thomas Gleixner <tglx@...utronix.de>
Subject: [RFC PATCH 2/2] kprobes/x86: Exit single-stepping before trying fixup_exception

Exit single-stepping out of line and get back regs->ip to original
(probed) address before trying fixup_exception() if the exception
happened on the singlestep buffer, since the fixup_exception()
depends on regs->ip to search an entry on __ex_table.

Signed-off-by: Masami Hiramatsu <mhiramat@...nel.org>
---
 arch/x86/include/asm/kprobes.h |    1 
 arch/x86/kernel/kprobes/core.c |   83 +++++++++++++++++++++++++---------------
 arch/x86/kernel/traps.c        |   19 +++++++++
 3 files changed, 71 insertions(+), 32 deletions(-)

diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h
index d1d1e50..79e121a 100644
--- a/arch/x86/include/asm/kprobes.h
+++ b/arch/x86/include/asm/kprobes.h
@@ -111,6 +111,7 @@ struct kprobe_ctlblk {
 	struct prev_kprobe prev_kprobe;
 };
 
+extern int kprobe_exit_singlestep(struct pt_regs *regs);
 extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 34d3a52..f2a3f3b 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -949,43 +949,62 @@ int kprobe_debug_handler(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(kprobe_debug_handler);
 
-int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+/* Fixup current ip register and reset current kprobe, if needed. */
+int kprobe_exit_singlestep(struct pt_regs *regs)
 {
-	struct kprobe *cur = kprobe_running();
 	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+	struct kprobe *cur = kprobe_running();
 
-	if (unlikely(regs->ip == (unsigned long)cur->ainsn.insn)) {
-		/* This must happen on single-stepping */
-		WARN_ON(kcb->kprobe_status != KPROBE_HIT_SS &&
-			kcb->kprobe_status != KPROBE_REENTER);
-		/*
-		 * We are here because the instruction being single
-		 * stepped caused a page fault. We reset the current
-		 * kprobe and the ip points back to the probe address
-		 * and allow the page fault handler to continue as a
-		 * 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 (unlikely(regs->ip != (unsigned long)cur->ainsn.insn))
+		return 0;
 
-		/*
-		 * If the TF flag was set before the kprobe hit,
-		 * don't touch it:
-		 */
-		regs->flags |= kcb->kprobe_old_flags;
+	/* This must happen on single-stepping */
+	WARN_ON(kcb->kprobe_status != KPROBE_HIT_SS &&
+		kcb->kprobe_status != KPROBE_REENTER);
+	/*
+	 * We are here because the instruction being single
+	 * stepped caused a page fault. We reset the current
+	 * kprobe and the ip points back to the probe address
+	 * and allow the page fault handler to continue as a
+	 * 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 (kcb->kprobe_status == KPROBE_REENTER)
-			restore_previous_kprobe(kcb);
-		else
-			reset_current_kprobe();
-		preempt_enable_no_resched();
-	} else if (kcb->kprobe_status == KPROBE_HIT_ACTIVE ||
-		   kcb->kprobe_status == KPROBE_HIT_SSDONE) {
+	/*
+	 * 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
+		reset_current_kprobe();
+
+	/* Preempt has been disabled before single stepping */
+	preempt_enable_no_resched();
+
+	return 1;
+}
+NOKPROBE_SYMBOL(kprobe_exit_singlestep);
+
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+	struct kprobe *cur = kprobe_running();
+
+	/* If the fault happened on singlestep, finish it and retry */
+	if (kprobe_exit_singlestep(regs))
+		return 0;
+
+	if (kcb->kprobe_status == KPROBE_HIT_ACTIVE ||
+	    kcb->kprobe_status == KPROBE_HIT_SSDONE) {
 		/*
 		 * We increment the nmissed count for accounting,
 		 * we can also use npre/npostfault count for accounting
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 948443e..7ac1baf 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -187,6 +187,14 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
 	}
 
 	if (!user_mode(regs)) {
+		/*
+		 * Exit from the kprobe's single-stepping before trying
+		 * fixup_exception() because the fixup routine is based on
+		 * trapped address (regs->ip). Single-stepping out of line
+		 * executes an instruction in different place, so it should
+		 * be fixed.
+		 */
+		kprobe_exit_singlestep(regs);
 		if (!fixup_exception(regs, trapnr)) {
 			tsk->thread.error_code = error_code;
 			tsk->thread.trap_nr = trapnr;
@@ -500,6 +508,12 @@ do_general_protection(struct pt_regs *regs, long error_code)
 
 	tsk = current;
 	if (!user_mode(regs)) {
+		/*
+		 * Exit from the kprobe's single-stepping before trying
+		 * fixup_exception(). Note that if the GPF occurred in
+		 * kprobe user handlers, it is handled in notify_die.
+		 */
+		kprobe_exit_singlestep(regs);
 		if (fixup_exception(regs, X86_TRAP_GP))
 			return;
 
@@ -799,6 +813,11 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
 	cond_local_irq_enable(regs);
 
 	if (!user_mode(regs)) {
+		/*
+		 * Exit from the kprobe's single-stepping before trying
+		 * fixup_exception().
+		 */
+		kprobe_exit_singlestep(regs);
 		if (!fixup_exception(regs, trapnr)) {
 			task->thread.error_code = error_code;
 			task->thread.trap_nr = trapnr;

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ