Ivan T. Ivanov 08b313
From: Masami Hiramatsu <mhiramat@kernel.org>
Ivan T. Ivanov 08b313
Date: Thu, 1 Aug 2019 23:25:49 +0900
Ivan T. Ivanov 08b313
Subject: arm64: kprobes: Recover pstate.D in single-step exception handler
Ivan T. Ivanov 08b313
Git-commit: b3980e48528c4d2a9e70b145a5bba328b73a0f93
Ivan T. Ivanov 08b313
Patch-mainline: v5.3-rc3
Ivan T. Ivanov 08b313
References: git-fixes
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
kprobes manipulates the interrupted PSTATE for single step, and
Ivan T. Ivanov 08b313
doesn't restore it. Thus, if we put a kprobe where the pstate.D
Ivan T. Ivanov 08b313
(debug) masked, the mask will be cleared after the kprobe hits.
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
Moreover, in the most complicated case, this can lead a kernel
Ivan T. Ivanov 08b313
crash with below message when a nested kprobe hits.
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
[  152.118921] Unexpected kernel single-step exception at EL1
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
When the 1st kprobe hits, do_debug_exception() will be called.
Ivan T. Ivanov 08b313
At this point, debug exception (= pstate.D) must be masked (=1).
Ivan T. Ivanov 08b313
But if another kprobes hits before single-step of the first kprobe
Ivan T. Ivanov 08b313
(e.g. inside user pre_handler), it unmask the debug exception
Ivan T. Ivanov 08b313
(pstate.D = 0) and return.
Ivan T. Ivanov 08b313
Then, when the 1st kprobe setting up single-step, it saves current
Ivan T. Ivanov 08b313
DAIF, mask DAIF, enable single-step, and restore DAIF.
Ivan T. Ivanov 08b313
However, since "D" flag in DAIF is cleared by the 2nd kprobe, the
Ivan T. Ivanov 08b313
single-step exception happens soon after restoring DAIF.
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
This has been introduced by commit 7419333fa15e ("arm64: kprobe:
Ivan T. Ivanov 08b313
Always clear pstate.D in breakpoint exception handler")
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
To solve this issue, this stores all DAIF bits and restore it
Ivan T. Ivanov 08b313
after single stepping.
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Ivan T. Ivanov 08b313
Fixes: 7419333fa15e ("arm64: kprobe: Always clear pstate.D in breakpoint exception handler")
Ivan T. Ivanov 08b313
Reviewed-by: James Morse <james.morse@arm.com>
Ivan T. Ivanov 08b313
Tested-by: James Morse <james.morse@arm.com>
Ivan T. Ivanov 08b313
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Ivan T. Ivanov 08b313
Signed-off-by: Will Deacon <will@kernel.org>
Ivan T. Ivanov 08b313
Acked-by: Ivan T. Ivanov <iivanov@suse.de>
Ivan T. Ivanov 08b313
---
Ivan T. Ivanov 08b313
 arch/arm64/include/asm/daifflags.h |    1 
Ivan T. Ivanov 08b313
 arch/arm64/kernel/probes/kprobes.c |   40 +++++--------------------------------
Ivan T. Ivanov 08b313
 2 files changed, 7 insertions(+), 34 deletions(-)
Ivan T. Ivanov 08b313
Ivan T. Ivanov 08b313
--- a/arch/arm64/include/asm/daifflags.h
Ivan T. Ivanov 08b313
+++ b/arch/arm64/include/asm/daifflags.h
Ivan T. Ivanov 08b313
@@ -20,6 +20,7 @@
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 #define DAIF_PROCCTX		0
Ivan T. Ivanov 08b313
 #define DAIF_PROCCTX_NOIRQ	PSR_I_BIT
Ivan T. Ivanov 08b313
+#define DAIF_MASK		(PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 /* mask/save/unmask/restore all exceptions, including interrupts. */
Ivan T. Ivanov 08b313
 static inline void local_daif_mask(void)
Ivan T. Ivanov 08b313
--- a/arch/arm64/kernel/probes/kprobes.c
Ivan T. Ivanov 08b313
+++ b/arch/arm64/kernel/probes/kprobes.c
Ivan T. Ivanov 08b313
@@ -28,6 +28,7 @@
Ivan T. Ivanov 08b313
 #include <asm/ptrace.h>
Ivan T. Ivanov 08b313
 #include <asm/cacheflush.h>
Ivan T. Ivanov 08b313
 #include <asm/debug-monitors.h>
Ivan T. Ivanov 08b313
+#include <asm/daifflags.h>
Ivan T. Ivanov 08b313
 #include <asm/system_misc.h>
Ivan T. Ivanov 08b313
 #include <asm/insn.h>
Ivan T. Ivanov 08b313
 #include <linux/uaccess.h>
Ivan T. Ivanov 08b313
@@ -167,33 +168,6 @@ static void __kprobes set_current_kprobe
Ivan T. Ivanov 08b313
 }
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 /*
Ivan T. Ivanov 08b313
- * When PSTATE.D is set (masked), then software step exceptions can not be
Ivan T. Ivanov 08b313
- * generated.
Ivan T. Ivanov 08b313
- * SPSR's D bit shows the value of PSTATE.D immediately before the
Ivan T. Ivanov 08b313
- * exception was taken. PSTATE.D is set while entering into any exception
Ivan T. Ivanov 08b313
- * mode, however software clears it for any normal (none-debug-exception)
Ivan T. Ivanov 08b313
- * mode in the exception entry. Therefore, when we are entering into kprobe
Ivan T. Ivanov 08b313
- * breakpoint handler from any normal mode then SPSR.D bit is already
Ivan T. Ivanov 08b313
- * cleared, however it is set when we are entering from any debug exception
Ivan T. Ivanov 08b313
- * mode.
Ivan T. Ivanov 08b313
- * Since we always need to generate single step exception after a kprobe
Ivan T. Ivanov 08b313
- * breakpoint exception therefore we need to clear it unconditionally, when
Ivan T. Ivanov 08b313
- * we become sure that the current breakpoint exception is for kprobe.
Ivan T. Ivanov 08b313
- */
Ivan T. Ivanov 08b313
-static void __kprobes
Ivan T. Ivanov 08b313
-spsr_set_debug_flag(struct pt_regs *regs, int mask)
Ivan T. Ivanov 08b313
-{
Ivan T. Ivanov 08b313
-	unsigned long spsr = regs->pstate;
Ivan T. Ivanov 08b313
-
Ivan T. Ivanov 08b313
-	if (mask)
Ivan T. Ivanov 08b313
-		spsr |= PSR_D_BIT;
Ivan T. Ivanov 08b313
-	else
Ivan T. Ivanov 08b313
-		spsr &= ~PSR_D_BIT;
Ivan T. Ivanov 08b313
-
Ivan T. Ivanov 08b313
-	regs->pstate = spsr;
Ivan T. Ivanov 08b313
-}
Ivan T. Ivanov 08b313
-
Ivan T. Ivanov 08b313
-/*
Ivan T. Ivanov 08b313
  * Interrupts need to be disabled before single-step mode is set, and not
Ivan T. Ivanov 08b313
  * reenabled until after single-step mode ends.
Ivan T. Ivanov 08b313
  * Without disabling interrupt on local CPU, there is a chance of
Ivan T. Ivanov 08b313
@@ -204,17 +178,17 @@ spsr_set_debug_flag(struct pt_regs *regs
Ivan T. Ivanov 08b313
 static void __kprobes kprobes_save_local_irqflag(struct kprobe_ctlblk *kcb,
Ivan T. Ivanov 08b313
 						struct pt_regs *regs)
Ivan T. Ivanov 08b313
 {
Ivan T. Ivanov 08b313
-	kcb->saved_irqflag = regs->pstate;
Ivan T. Ivanov 08b313
+	kcb->saved_irqflag = regs->pstate & DAIF_MASK;
Ivan T. Ivanov 08b313
 	regs->pstate |= PSR_I_BIT;
Ivan T. Ivanov 08b313
+	/* Unmask PSTATE.D for enabling software step exceptions. */
Ivan T. Ivanov 08b313
+	regs->pstate &= ~PSR_D_BIT;
Ivan T. Ivanov 08b313
 }
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 static void __kprobes kprobes_restore_local_irqflag(struct kprobe_ctlblk *kcb,
Ivan T. Ivanov 08b313
 						struct pt_regs *regs)
Ivan T. Ivanov 08b313
 {
Ivan T. Ivanov 08b313
-	if (kcb->saved_irqflag & PSR_I_BIT)
Ivan T. Ivanov 08b313
-		regs->pstate |= PSR_I_BIT;
Ivan T. Ivanov 08b313
-	else
Ivan T. Ivanov 08b313
-		regs->pstate &= ~PSR_I_BIT;
Ivan T. Ivanov 08b313
+	regs->pstate &= ~DAIF_MASK;
Ivan T. Ivanov 08b313
+	regs->pstate |= kcb->saved_irqflag;
Ivan T. Ivanov 08b313
 }
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 static void __kprobes
Ivan T. Ivanov 08b313
@@ -251,8 +225,6 @@ static void __kprobes setup_singlestep(s
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
 		set_ss_context(kcb, slot);	/* mark pending ss */
Ivan T. Ivanov 08b313
 
Ivan T. Ivanov 08b313
-		spsr_set_debug_flag(regs, 0);
Ivan T. Ivanov 08b313
-
Ivan T. Ivanov 08b313
 		/* IRQs and single stepping do not mix well. */
Ivan T. Ivanov 08b313
 		kprobes_save_local_irqflag(kcb, regs);
Ivan T. Ivanov 08b313
 		kernel_enable_single_step(regs);