From e5f6f741370711ffeb8dae3b81c975960551f3b8 Mon Sep 17 00:00:00 2001 From: Evan Chan Date: Mon, 16 Mar 2026 16:38:47 +0800 Subject: [PATCH 1/2] ptraceomatic: loop until eip changes to handle page faults When cpu_run_to_interrupt returns INT_GPF (e.g. stack growth), the faulting instruction doesn't execute and eip doesn't advance. handle_interrupt maps the page, but step_tracing then stepped the real CPU forward, creating a permanent eip desync. Loop until eip changes, matching the existing real-CPU pattern for repeated string instructions. This also handles the case where a page fault goes to a signal handler. Co-Authored-By: Claude Opus 4.6 (1M context) --- tools/ptraceomatic.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tools/ptraceomatic.c b/tools/ptraceomatic.c index 9276a2cba6..9da5ed8cc4 100644 --- a/tools/ptraceomatic.c +++ b/tools/ptraceomatic.c @@ -254,17 +254,21 @@ static void pt_copy_to_real(int pid, addr_t start, size_t size) { static void step_tracing(struct cpu_state *cpu, struct tlb *tlb, int pid, int sender, int receiver) { // step fake cpu - cpu->tf = 1; - int interrupt = cpu_run_to_interrupt(cpu, tlb); - // hack to clean up before the exit syscall - if (interrupt == INT_SYSCALL && cpu->eax == 1) { - if (kill(pid, SIGKILL) < 0) { - perror("kill tracee during exit"); - exit(1); + // loop until ip changes, to match ptrace step. also because page faults that get handled in the kernel do escape cpu_run_to_interrupt but aren't visible from ptrace_step. + dword_t ip = cpu->eip; + while (cpu->eip == ip) { + cpu->tf = 1; + int interrupt = cpu_run_to_interrupt(cpu, tlb); + // hack to clean up before the exit syscall + if (interrupt == INT_SYSCALL && cpu->eax == 1) { + if (kill(pid, SIGKILL) < 0) { + perror("kill tracee during exit"); + exit(1); + } } + if (interrupt != INT_DEBUG) + handle_interrupt(interrupt); } - if (interrupt != INT_DEBUG) - handle_interrupt(interrupt); // step real cpu // intercept cpuid, rdtsc, and int $0x80, though From 6f7f72b836d5c03e94891e5eaf02b93afe729e28 Mon Sep 17 00:00:00 2001 From: Evan Chan Date: Mon, 16 Mar 2026 16:39:13 +0800 Subject: [PATCH 2/2] ptraceomatic: skip F2/F3 prefixes in undefined_flags_mask Only the 0x66 prefix was skipped when looking up the opcode to determine undefined flags. This caused tzcnt (F3 0F BC, decoded as rep bsf on non-BMI1 CPUs) to not get its flags (O,S,A,P,C) marked as undefined, triggering a false eflags mismatch. Co-Authored-By: Claude Opus 4.6 (1M context) --- tools/undefined-flags.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/undefined-flags.c b/tools/undefined-flags.c index fa6f6f23b2..25e4f848f5 100644 --- a/tools/undefined-flags.c +++ b/tools/undefined-flags.c @@ -74,7 +74,7 @@ int undefined_flags_mask(struct cpu_state *cpu, struct tlb *tlb) { } break; } - case 0x66: goto skip; + case 0x66: case 0xf2: case 0xf3: goto skip; } return 0; }