]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/fred/signal: Prevent immediate repeat of single step trap on return from SIGTRAP...
authorXin Li (Intel) <xin@zytor.com>
Mon, 9 Jun 2025 08:40:53 +0000 (01:40 -0700)
committerDave Hansen <dave.hansen@linux.intel.com>
Mon, 9 Jun 2025 15:50:58 +0000 (08:50 -0700)
Clear the software event flag in the augmented SS to prevent immediate
repeat of single step trap on return from SIGTRAP handler if the trap
flag (TF) is set without an external debugger attached.

Following is a typical single-stepping flow for a user process:

1) The user process is prepared for single-stepping by setting
   RFLAGS.TF = 1.
2) When any instruction in user space completes, a #DB is triggered.
3) The kernel handles the #DB and returns to user space, invoking the
   SIGTRAP handler with RFLAGS.TF = 0.
4) After the SIGTRAP handler finishes, the user process performs a
   sigreturn syscall, restoring the original state, including
   RFLAGS.TF = 1.
5) Goto step 2.

According to the FRED specification:

A) Bit 17 in the augmented SS is designated as the software event
   flag, which is set to 1 for FRED event delivery of SYSCALL,
   SYSENTER, or INT n.
B) If bit 17 of the augmented SS is 1 and ERETU would result in
   RFLAGS.TF = 1, a single-step trap will be pending upon completion
   of ERETU.

In step 4) above, the software event flag is set upon the sigreturn
syscall, and its corresponding ERETU would restore RFLAGS.TF = 1.
This combination causes a pending single-step trap upon completion of
ERETU.  Therefore, another #DB is triggered before any user space
instruction is executed, which leads to an infinite loop in which the
SIGTRAP handler keeps being invoked on the same user space IP.

Fixes: 14619d912b65 ("x86/fred: FRED entry/exit and dispatch code")
Suggested-by: H. Peter Anvin (Intel) <hpa@zytor.com>
Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Tested-by: Sohil Mehta <sohil.mehta@intel.com>
Cc:stable@vger.kernel.org
Link: https://lore.kernel.org/all/20250609084054.2083189-2-xin%40zytor.com
arch/x86/include/asm/sighandling.h
arch/x86/kernel/signal_32.c
arch/x86/kernel/signal_64.c

index e770c4fc47f4c5d9af4cb5ce9971c1a16d2e4946..8727c7e21dd1e680d48a1185936355f204b0be35 100644 (file)
@@ -24,4 +24,26 @@ int ia32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
 int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
 int x32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
 
+/*
+ * To prevent immediate repeat of single step trap on return from SIGTRAP
+ * handler if the trap flag (TF) is set without an external debugger attached,
+ * clear the software event flag in the augmented SS, ensuring no single-step
+ * trap is pending upon ERETU completion.
+ *
+ * Note, this function should be called in sigreturn() before the original
+ * state is restored to make sure the TF is read from the entry frame.
+ */
+static __always_inline void prevent_single_step_upon_eretu(struct pt_regs *regs)
+{
+       /*
+        * If the trap flag (TF) is set, i.e., the sigreturn() SYSCALL instruction
+        * is being single-stepped, do not clear the software event flag in the
+        * augmented SS, thus a debugger won't skip over the following instruction.
+        */
+#ifdef CONFIG_X86_FRED
+       if (!(regs->flags & X86_EFLAGS_TF))
+               regs->fred_ss.swevent = 0;
+#endif
+}
+
 #endif /* _ASM_X86_SIGHANDLING_H */
index 98123ff10506c6e191530ac1ffe96f44fba1d0e4..42bbc42bd3503cbd733079c80548f8f3e65b001a 100644 (file)
@@ -152,6 +152,8 @@ SYSCALL32_DEFINE0(sigreturn)
        struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
        sigset_t set;
 
+       prevent_single_step_upon_eretu(regs);
+
        if (!access_ok(frame, sizeof(*frame)))
                goto badframe;
        if (__get_user(set.sig[0], &frame->sc.oldmask)
@@ -175,6 +177,8 @@ SYSCALL32_DEFINE0(rt_sigreturn)
        struct rt_sigframe_ia32 __user *frame;
        sigset_t set;
 
+       prevent_single_step_upon_eretu(regs);
+
        frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
 
        if (!access_ok(frame, sizeof(*frame)))
index ee9453891901b71d6958b135848dc9cdeff0e364..d483b585c6c6048cec5aaf0c0484963498867b14 100644 (file)
@@ -250,6 +250,8 @@ SYSCALL_DEFINE0(rt_sigreturn)
        sigset_t set;
        unsigned long uc_flags;
 
+       prevent_single_step_upon_eretu(regs);
+
        frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
        if (!access_ok(frame, sizeof(*frame)))
                goto badframe;
@@ -366,6 +368,8 @@ COMPAT_SYSCALL_DEFINE0(x32_rt_sigreturn)
        sigset_t set;
        unsigned long uc_flags;
 
+       prevent_single_step_upon_eretu(regs);
+
        frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8);
 
        if (!access_ok(frame, sizeof(*frame)))