Download raw body.
arm64: fix PT_GETFPREGS/PT_SETFPREGS for traced processes
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 <stdio.h>
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 <repeats 16 times>}
$2 = {0x0 <repeats 16 times>}
$3 = {0x0 <repeats 16 times>}
process sees v8=0x00 v9=0x00
After:
$1 = {0x42 <repeats 16 times>}
$2 = {0x17 <repeats 16 times>}
$3 = {0x55, 0x17 <repeats 15 times>}
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);
}
arm64: fix PT_GETFPREGS/PT_SETFPREGS for traced processes