From: Brad Smith Subject: arm64: fix PT_GETFPREGS/PT_SETFPREGS for traced processes To: tech@openbsd.org Cc: kettenis@openbsd.org Date: Mon, 26 Oct 2026 05:33:40 -0400 process_read_fpregs() and process_write_fpregs() call fpu_save() and fpu_drop(), which operate on the FPU of the current CPU. For ptrace(2) that FPU belongs to the debugger, not the traced process, so PT_GETFPREGS copied the debugger's live FP/SIMD registers into the traced process and returned them, and PT_SETFPREGS discarded the debugger's live FPU state. The FPU state of a process that is not running is already saved in its pcb by cpu_switchto(), so only save or drop the live state when the process is curproc. This affects any debugger on arm64 (egdb, lldb). Simply reading an FP register from the debugger corrupts the traced process's FP state, and the debugger can lose its own callee-saved d8-d15. Reproducer: fp2.c: #include int main(void) { unsigned char v8[16], v9[16]; __asm__ volatile("movi v8.16b, #0x42\n\tmovi v9.16b, #0x17" ::: "v8", "v9"); __asm__ volatile("nop"); /* break here */ __asm__ volatile("str q8, [%0]\n\tstr q9, [%1]" :: "r"(v8), "r"(v9) : "memory"); printf("process sees v8=0x%02x v9=0x%02x\n", v8[0], v9[0]); return 0; } $ cc -g -O0 -o fp2 fp2.c $ ./fp2 process sees v8=0x42 v9=0x17 Before: $ egdb -q -batch -ex 'break 5' -ex run -ex 'p/x $v8.b.u' -ex 'p/x $v9.b.u' \ -ex 'set var $v9.b.u[0] = 0x55' -ex 'p/x $v9.b.u' -ex continue ./fp2 ... $1 = {0x0 } $2 = {0x0 } $3 = {0x0 } process sees v8=0x00 v9=0x00 After: $1 = {0x42 } $2 = {0x17 } $3 = {0x55, 0x17 } process sees v8=0x42 v9=0x55 --- a/sys/arch/arm64/arm64/process_machdep.c +++ b/sys/arch/arm64/arm64/process_machdep.c @@ -70,7 +70,11 @@ int process_read_fpregs(struct proc *p, struct fpreg *regs) { - if (p->p_addr->u_pcb.pcb_flags & PCB_FPU) + /* + * The FPU state of a process that is not running was saved when + * it was switched out; only curproc can have live state. + */ + if (p == curproc && (p->p_addr->u_pcb.pcb_flags & PCB_FPU)) fpu_save(p); if (p->p_addr->u_pcb.pcb_flags & PCB_FPU) @@ -105,7 +109,8 @@ p->p_addr->u_pcb.pcb_flags |= PCB_FPU; /* drop FPU state */ - fpu_drop(); + if (p == curproc) + fpu_drop(); return(0); }