Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/fred/signal: Prevent immediate repeat of single step trap on return from SIGTRAP handler

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

authored by

Xin Li (Intel) and committed by
Dave Hansen
e34dbbc8 19272b37

+30
+22
arch/x86/include/asm/sighandling.h
··· 24 24 int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs); 25 25 int x32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs); 26 26 27 + /* 28 + * To prevent immediate repeat of single step trap on return from SIGTRAP 29 + * handler if the trap flag (TF) is set without an external debugger attached, 30 + * clear the software event flag in the augmented SS, ensuring no single-step 31 + * trap is pending upon ERETU completion. 32 + * 33 + * Note, this function should be called in sigreturn() before the original 34 + * state is restored to make sure the TF is read from the entry frame. 35 + */ 36 + static __always_inline void prevent_single_step_upon_eretu(struct pt_regs *regs) 37 + { 38 + /* 39 + * If the trap flag (TF) is set, i.e., the sigreturn() SYSCALL instruction 40 + * is being single-stepped, do not clear the software event flag in the 41 + * augmented SS, thus a debugger won't skip over the following instruction. 42 + */ 43 + #ifdef CONFIG_X86_FRED 44 + if (!(regs->flags & X86_EFLAGS_TF)) 45 + regs->fred_ss.swevent = 0; 46 + #endif 47 + } 48 + 27 49 #endif /* _ASM_X86_SIGHANDLING_H */
+4
arch/x86/kernel/signal_32.c
··· 152 152 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8); 153 153 sigset_t set; 154 154 155 + prevent_single_step_upon_eretu(regs); 156 + 155 157 if (!access_ok(frame, sizeof(*frame))) 156 158 goto badframe; 157 159 if (__get_user(set.sig[0], &frame->sc.oldmask) ··· 176 174 struct pt_regs *regs = current_pt_regs(); 177 175 struct rt_sigframe_ia32 __user *frame; 178 176 sigset_t set; 177 + 178 + prevent_single_step_upon_eretu(regs); 179 179 180 180 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4); 181 181
+4
arch/x86/kernel/signal_64.c
··· 250 250 sigset_t set; 251 251 unsigned long uc_flags; 252 252 253 + prevent_single_step_upon_eretu(regs); 254 + 253 255 frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); 254 256 if (!access_ok(frame, sizeof(*frame))) 255 257 goto badframe; ··· 367 365 struct rt_sigframe_x32 __user *frame; 368 366 sigset_t set; 369 367 unsigned long uc_flags; 368 + 369 + prevent_single_step_upon_eretu(regs); 370 370 371 371 frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); 372 372