Skip to content

Commit e34dbbc

Browse files
xinli-intelhansendc
authored andcommitted
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: 14619d9 ("x86/fred: FRED entry/exit and dispatch code") Suggested-by: H. Peter Anvin (Intel) <[email protected]> Signed-off-by: Xin Li (Intel) <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Tested-by: Sohil Mehta <[email protected]> Cc:[email protected] Link: https://lore.kernel.org/all/20250609084054.2083189-2-xin%40zytor.com
1 parent 19272b3 commit e34dbbc

File tree

3 files changed

+30
-0
lines changed

3 files changed

+30
-0
lines changed

arch/x86/include/asm/sighandling.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,26 @@ int ia32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
2424
int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
2525
int x32_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs);
2626

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+
2749
#endif /* _ASM_X86_SIGHANDLING_H */

arch/x86/kernel/signal_32.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ SYSCALL32_DEFINE0(sigreturn)
152152
struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
153153
sigset_t set;
154154

155+
prevent_single_step_upon_eretu(regs);
156+
155157
if (!access_ok(frame, sizeof(*frame)))
156158
goto badframe;
157159
if (__get_user(set.sig[0], &frame->sc.oldmask)
@@ -175,6 +177,8 @@ SYSCALL32_DEFINE0(rt_sigreturn)
175177
struct rt_sigframe_ia32 __user *frame;
176178
sigset_t set;
177179

180+
prevent_single_step_upon_eretu(regs);
181+
178182
frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
179183

180184
if (!access_ok(frame, sizeof(*frame)))

arch/x86/kernel/signal_64.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ SYSCALL_DEFINE0(rt_sigreturn)
250250
sigset_t set;
251251
unsigned long uc_flags;
252252

253+
prevent_single_step_upon_eretu(regs);
254+
253255
frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
254256
if (!access_ok(frame, sizeof(*frame)))
255257
goto badframe;
@@ -366,6 +368,8 @@ COMPAT_SYSCALL_DEFINE0(x32_rt_sigreturn)
366368
sigset_t set;
367369
unsigned long uc_flags;
368370

371+
prevent_single_step_upon_eretu(regs);
372+
369373
frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8);
370374

371375
if (!access_ok(frame, sizeof(*frame)))

0 commit comments

Comments
 (0)