[PATCH v2] x86/traps: Handle trap flag when instruction is emulated

Samuele Cerea posted 1 patch 1 month ago
arch/x86/include/asm/traps.h |  2 ++
arch/x86/kernel/traps.c      | 14 ++++++++++++++
arch/x86/kernel/umip.c       |  1 +
3 files changed, 17 insertions(+)
[PATCH v2] x86/traps: Handle trap flag when instruction is emulated
Posted by Samuele Cerea 1 month ago
Simulate the trap flag (TF) behavior when the kernel emulates UIMP
instructions and iopl instructions.

When an instruction is emulated successfully and the TF is set, send a
SIGTRAP signal to the process, as it would happen for a normally
executed instruction.

Fix a problem with debuggers when signle-stepping instructions where
emulated instruction would get skipped.

Here is an example of the problem:
    NOP
    SLDT %rax
    SLDT %rax
    NOP
The two SLDT instructions will be skipped an the debugger will step
directly to the second NOP instruction.

Signed-off-by: Samuele Cerea <samuele@cerea.dev>
---
I fixed the issues you pointed out, hopefully now everything is correct

 arch/x86/include/asm/traps.h |  2 ++
 arch/x86/kernel/traps.c      | 14 ++++++++++++++
 arch/x86/kernel/umip.c       |  1 +
 3 files changed, 17 insertions(+)

diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 869b88061801..7742a6d05158 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -39,6 +39,8 @@ void math_emulate(struct math_emu_info *);
 
 bool fault_in_kernel_space(unsigned long address);
 
+void emulate_trap_flag(struct pt_regs *regs);
+
 #ifdef CONFIG_VMAP_STACK
 void __noreturn handle_stack_overflow(struct pt_regs *regs,
 				      unsigned long fault_address,
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 36354b470590..bea28473866b 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -678,6 +678,19 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
 
 #define GPFSTR "general protection fault"
 
+void emulate_trap_flag(struct pt_regs *regs)
+{
+	struct task_struct *tsk = current;
+
+	/* If the instruction was emulated successfully, emulate trap flag */
+	if (regs->flags & X86_EFLAGS_TF) {
+		tsk->thread.cr2 = regs->ip;
+		tsk->thread.trap_nr = X86_TRAP_DB;
+		tsk->thread.error_code = 0;
+		force_sig_fault(SIGTRAP, TRAP_TRACE, (void __user *)regs->ip);
+	}
+}
+
 static bool fixup_iopl_exception(struct pt_regs *regs)
 {
 	struct thread_struct *t = &current->thread;
@@ -705,6 +718,7 @@ static bool fixup_iopl_exception(struct pt_regs *regs)
 	}
 
 	regs->ip += 1;
+	emulate_trap_flag(regs);
 	return true;
 }
 
diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 5a4b21389b1d..8a5f33562bb4 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -407,5 +407,6 @@ bool fixup_umip_exception(struct pt_regs *regs)
 
 	/* increase IP to let the program keep going */
 	regs->ip += insn.length;
+	emulate_trap_flag(regs);
 	return true;
 }
-- 
2.51.0
Re: [PATCH v2] x86/traps: Handle trap flag when instruction is emulated
Posted by Andrew Cooper 1 month ago
> Simulate the trap flag (TF) behavior when the kernel emulates UIMP
> instructions and iopl instructions.

You'll want to do this for CPUID too when CPUID Faulting is active.

> diff
> <https://lore.kernel.org/lkml/20250901162527.18247-2-samuele@cerea.dev/#iZ31arch:x86:kernel:traps.c>
> --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index
> 36354b470590..bea28473866b 100644 --- a/arch/x86/kernel/traps.c +++
> b/arch/x86/kernel/traps.c
> @@ -705,6 +718,7 @@ static bool fixup_iopl_exception(struct pt_regs
> *regs)  	}
>  
>  	regs->ip += 1;
> + emulate_trap_flag(regs);  	return true;
>  }
>  

There's a fun bug in fixup_iopl_exception() which you're turning from
latent to real.

Not all STI/CLI instructions are 1 byte long.  If they have a redundant
prefix, the old logic would simply brute-force through the instruction 1
prefix byte at a time.

But now, you'll generate SIGTRAP in the middle of the instruction.

~Andrew