diff mbox series

[v2,2/4] arm64: ptrace: Consistently use pseudo-singlestep exceptions

Message ID 20200702212618.17800-3-will@kernel.org (mailing list archive)
State Mainlined
Commit ac2081cdc4d99c57f219c1a6171526e0fa0a6fff
Headers show
Series arm64: Fix single-step handling | expand

Commit Message

Will Deacon July 2, 2020, 9:26 p.m. UTC
Although the arm64 single-step state machine can be fast-forwarded in
cases where we wish to generate a SIGTRAP without actually executing an
instruction, this has two major limitations outside of simply skipping
an instruction due to emulation.

1. Stepping out of a ptrace signal stop into a signal handler where
   SIGTRAP is blocked. Fast-forwarding the stepping state machine in
   this case will result in a forced SIGTRAP, with the handler reset to
   SIG_DFL.

2. The hardware implicitly fast-forwards the state machine when executing
   an SVC instruction for issuing a system call. This can interact badly
   with subsequent ptrace stops signalled during the execution of the
   system call (e.g. SYSCALL_EXIT or seccomp traps), as they may corrupt
   the stepping state by updating the PSTATE for the tracee.

Resolve both of these issues by injecting a pseudo-singlestep exception
on entry to a signal handler and also on return to userspace following a
system call.

Cc: <stable@vger.kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Luis Machado <luis.machado@linaro.org>
Reported-by: Keno Fischer <keno@juliacomputing.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/include/asm/thread_info.h |  1 +
 arch/arm64/kernel/ptrace.c           | 25 +++++++++++++++++++------
 arch/arm64/kernel/signal.c           | 11 ++---------
 arch/arm64/kernel/syscall.c          |  2 +-
 4 files changed, 23 insertions(+), 16 deletions(-)

Comments

Sasha Levin July 10, 2020, 2:02 p.m. UTC | #1
Hi

[This is an automated email]

This commit has been processed because it contains a -stable tag.
The stable tag indicates that it's relevant for the following trees: all

The bot has tested the following trees: v5.7.7, v5.4.50, v4.19.131, v4.14.187, v4.9.229, v4.4.229.

v5.7.7: Build OK!
v5.4.50: Build OK!
v4.19.131: Build OK!
v4.14.187: Failed to apply! Possible dependencies:
    0013aceb30748 ("xtensa: clean up fixups in assembly code")
    1af1e8a39dc0f ("xtensa: move fixmap and kmap just above the KSEG")
    2a61f4747eeaa ("stack-protector: test compiler capability in Kconfig and drop AUTO mode")
    2b8383927525d ("Makefile: move stack-protector compiler breakage test earlier")
    2bc2f688fdf88 ("Makefile: move stack-protector availability out of Kconfig")
    409d5db49867c ("arm64: rseq: Implement backend rseq calls and select HAVE_RSEQ")
    40d1a07b333ef ("xtensa: enable stack protector")
    44c6dc940b190 ("Makefile: introduce CONFIG_CC_STACKPROTECTOR_AUTO")
    5cf97ebd8b40e ("xtensa: clean up functions in assembly code")
    8d66772e869e7 ("arm64: Mask all exceptions during kernel_exit")
    9800b9dc13cdf ("arm: Add restartable sequences support")
    a92d4d1454ab8 ("arm64: entry.S: move SError handling into a C function for future expansion")
    c2edb35ae342f ("xtensa: extract init_kio")
    c633544a61541 ("xtensa: add support for KASAN")
    d148eac0e70f0 ("Kbuild: rename HAVE_CC_STACKPROTECTOR config variable")
    f37099b6992a0 ("arm64: convert syscall trace logic to C")
    f4431396be5b2 ("xtensa: consolidate kernel stack size related definitions")

v4.9.229: Failed to apply! Possible dependencies:
    096683724cb2e ("arm64: unwind: avoid percpu indirection for irq stack")
    12964443e8d19 ("arm64: add on_accessible_stack()")
    17c2895860092 ("arm64: Abstract syscallno manipulation")
    34be98f4944f9 ("arm64: kernel: remove {THREAD,IRQ_STACK}_START_SP")
    35d0e6fb4d219 ("arm64: syscallno is secretly an int, make it official")
    8018ba4edfd3a ("arm64: move SEGMENT_ALIGN to <asm/memory.h>")
    872d8327ce898 ("arm64: add VMAP_STACK overflow detection")
    9842ceae9fa8d ("arm64: Add uprobe support")
    a92d4d1454ab8 ("arm64: entry.S: move SError handling into a C function for future expansion")
    a9ea0017ebe88 ("arm64: factor out current_stack_pointer")
    c02433dd6de32 ("arm64: split thread_info from task stack")
    c7365330753c5 ("arm64: unwind: disregard frame.sp when validating frame pointer")
    cf7de27ab3517 ("arm64/syscalls: Check address limit on user-mode return")
    dbc9344a68e50 ("arm64: clean up THREAD_* definitions")
    f37099b6992a0 ("arm64: convert syscall trace logic to C")
    f60ad4edcf072 ("arm64: clean up irq stack definitions")

v4.4.229: Failed to apply! Possible dependencies:
    0a28714c53fd4 ("arm64: Use PoU cache instr for I/D coherency")
    0a8ea52c3eb15 ("arm64: Add HAVE_REGS_AND_STACK_ACCESS_API feature")
    17c2895860092 ("arm64: Abstract syscallno manipulation")
    2dd0e8d2d2a15 ("arm64: Kprobes with single stepping support")
    35d0e6fb4d219 ("arm64: syscallno is secretly an int, make it official")
    39a67d49ba353 ("arm64: kprobes instruction simulation support")
    421dd6fa6709e ("arm64: factor work_pending state machine to C")
    57f4959bad0a1 ("arm64: kernel: Add support for User Access Override")
    872d8327ce898 ("arm64: add VMAP_STACK overflow detection")
    9842ceae9fa8d ("arm64: Add uprobe support")
    a92d4d1454ab8 ("arm64: entry.S: move SError handling into a C function for future expansion")
    b11e5759bfac0 ("arm64: factor out entry stack manipulation")
    cf7de27ab3517 ("arm64/syscalls: Check address limit on user-mode return")
    d5370f7548754 ("arm64: prefetch: add alternative pattern for CPUs without a prefetcher")
    d59bee8872311 ("arm64: Add more test functions to insn.c")
    e04a28d45ff34 ("arm64: debug: re-enable irqs before sending breakpoint SIGTRAP")
    f37099b6992a0 ("arm64: convert syscall trace logic to C")


NOTE: The patch will not be queued to stable trees until it is upstream.

How should we proceed with this patch?
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 6ea8b6a26ae9..5e784e16ee89 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -93,6 +93,7 @@  void arch_release_task_struct(struct task_struct *tsk);
 #define _TIF_SYSCALL_EMU	(1 << TIF_SYSCALL_EMU)
 #define _TIF_UPROBE		(1 << TIF_UPROBE)
 #define _TIF_FSCHECK		(1 << TIF_FSCHECK)
+#define _TIF_SINGLESTEP		(1 << TIF_SINGLESTEP)
 #define _TIF_32BIT		(1 << TIF_32BIT)
 #define _TIF_SVE		(1 << TIF_SVE)
 
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index d71795dc3dbd..7b46caea1278 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -1830,12 +1830,23 @@  static void tracehook_report_syscall(struct pt_regs *regs,
 	saved_reg = regs->regs[regno];
 	regs->regs[regno] = dir;
 
-	if (dir == PTRACE_SYSCALL_EXIT)
+	if (dir == PTRACE_SYSCALL_ENTER) {
+		if (tracehook_report_syscall_entry(regs))
+			forget_syscall(regs);
+		regs->regs[regno] = saved_reg;
+	} else if (!test_thread_flag(TIF_SINGLESTEP)) {
 		tracehook_report_syscall_exit(regs, 0);
-	else if (tracehook_report_syscall_entry(regs))
-		forget_syscall(regs);
+		regs->regs[regno] = saved_reg;
+	} else {
+		regs->regs[regno] = saved_reg;
 
-	regs->regs[regno] = saved_reg;
+		/*
+		 * Signal a pseudo-step exception since we are stepping but
+		 * tracer modifications to the registers may have rewound the
+		 * state machine.
+		 */
+		tracehook_report_syscall_exit(regs, 1);
+	}
 }
 
 int syscall_trace_enter(struct pt_regs *regs)
@@ -1863,12 +1874,14 @@  int syscall_trace_enter(struct pt_regs *regs)
 
 void syscall_trace_exit(struct pt_regs *regs)
 {
+	unsigned long flags = READ_ONCE(current_thread_info()->flags);
+
 	audit_syscall_exit(regs);
 
-	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+	if (flags & _TIF_SYSCALL_TRACEPOINT)
 		trace_sys_exit(regs, regs_return_value(regs));
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE))
+	if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP))
 		tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
 
 	rseq_syscall(regs);
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 801d56cdf701..3b4f31f35e45 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -800,7 +800,6 @@  static void setup_restart_syscall(struct pt_regs *regs)
  */
 static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
 {
-	struct task_struct *tsk = current;
 	sigset_t *oldset = sigmask_to_save();
 	int usig = ksig->sig;
 	int ret;
@@ -824,14 +823,8 @@  static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
 	 */
 	ret |= !valid_user_regs(&regs->user_regs, current);
 
-	/*
-	 * Fast forward the stepping logic so we step into the signal
-	 * handler.
-	 */
-	if (!ret)
-		user_fastforward_single_step(tsk);
-
-	signal_setup_done(ret, ksig, 0);
+	/* Step into the signal handler if we are stepping */
+	signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 5f5b868292f5..7c14466a12af 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -139,7 +139,7 @@  static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 	if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
 		local_daif_mask();
 		flags = current_thread_info()->flags;
-		if (!has_syscall_work(flags)) {
+		if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP)) {
 			/*
 			 * We're off to userspace, where interrupts are
 			 * always enabled after we restore the flags from