Commit da018f9726

Alex Rønne Petersen <alex@alexrp.com>
2024-11-04 18:30:36
std.os.linux: Add unwinding protection in clone() implementations.
Whatever was in the frame pointer register prior to clone() will no longer be valid in the child process, so zero it to protect FP-based unwinders. Similarly, mark the link register as undefined to protect DWARF-based unwinders. This is only zeroing the frame pointer(s) on Arm/Thumb because of an LLVM assembler bug: https://github.com/llvm/llvm-project/issues/115891
1 parent 7d00877
lib/std/os/linux/aarch64.zig
@@ -120,8 +120,13 @@ pub fn clone() callconv(.Naked) usize {
         \\      cbz x0,1f
         \\      // parent
         \\      ret
+        \\
         \\      // child
-        \\1:    ldp x1,x0,[sp],#16
+        \\1:    .cfi_undefined lr
+        \\      mov fp, 0
+        \\      mov lr, 0
+        \\
+        \\      ldp x1,x0,[sp],#16
         \\      blr x1
         \\      mov x8,#93 // SYS_exit
         \\      svc #0
lib/std/os/linux/arm.zig
@@ -120,11 +120,16 @@ pub fn clone() callconv(.Naked) usize {
         \\    ldmfd sp!,{r4,r5,r6,r7}
         \\    bx lr
         \\
-        \\1:  mov r0,r6
+        \\    // https://github.com/llvm/llvm-project/issues/115891
+        \\1:  mov r7, #0
+        \\    mov r11, #0
+        \\    mov lr, #0
+        \\
+        \\    mov r0,r6
         \\    bl 3f
-        \\2:  mov r7,#1 // SYS_exit
+        \\    mov r7,#1 // SYS_exit
         \\    svc 0
-        \\    b 2b
+        \\
         \\3:  bx r5
     );
 }
lib/std/os/linux/hexagon.zig
@@ -118,6 +118,10 @@ pub fn clone() callconv(.Naked) usize {
         \\ p0 = cmp.eq(r0, #0)
         \\ if (!p0) dealloc_return
         \\
+        \\ .cfi_undefined r31
+        \\ r30 = #0
+        \\ r31 = #0
+        \\
         \\ r0 = r10
         \\ callr r11
         \\
lib/std/os/linux/loongarch64.zig
@@ -121,6 +121,10 @@ pub fn clone() callconv(.Naked) usize {
         \\ beqz    $a0, 1f         # whether child process
         \\ jirl    $zero, $ra, 0   # parent process return
         \\1:
+        \\ .cfi_undefined 1
+        \\ move    $fp, $zero
+        \\ move    $ra, $zero
+        \\
         \\ ld.d    $t8, $sp, 0     # function pointer
         \\ ld.d    $a0, $sp, 8     # argument pointer
         \\ jirl    $ra, $t8, 0     # call the user's function
lib/std/os/linux/mips.zig
@@ -231,6 +231,10 @@ pub fn clone() callconv(.Naked) usize {
         \\  jr $ra
         \\  nop
         \\1:
+        \\  .cfi_undefined $ra
+        \\  move $fp, $zero
+        \\  move $ra, $zero
+        \\
         \\  lw $25, 0($sp)
         \\  lw $4, 4($sp)
         \\  jalr $25
lib/std/os/linux/mips64.zig
@@ -210,6 +210,10 @@ pub fn clone() callconv(.Naked) usize {
         \\ jr $ra
         \\ nop
         \\1:
+        \\ .cfi_undefined $ra
+        \\ move $fp, $zero
+        \\ move $ra, $zero
+        \\
         \\ ld $25, 0($sp)
         \\ ld $4, 8($sp)
         \\ jalr $25
lib/std/os/linux/powerpc.zig
@@ -133,14 +133,14 @@ pub fn clone() callconv(.Naked) usize {
     // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
     //         0          3,     4,     5,    6,   7
     asm volatile (
-        \\ # store non-volatile regs r30, r31 on stack in order to put our
+        \\ # store non-volatile regs r29, r30 on stack in order to put our
         \\ # start func and its arg there
-        \\ stwu 30, -16(1)
-        \\ stw 31, 4(1)
+        \\ stwu 29, -16(1)
+        \\ stw 30, 4(1)
         \\
-        \\ # save r3 (func) into r30, and r6(arg) into r31
-        \\ mr 30, 3
-        \\ mr 31, 6
+        \\ # save r3 (func) into r29, and r6(arg) into r30
+        \\ mr 29, 3
+        \\ mr 30, 6
         \\
         \\ # create initial stack frame for new thread
         \\ clrrwi 4, 4, 4
@@ -167,28 +167,28 @@ pub fn clone() callconv(.Naked) usize {
         \\ # compare sc result with 0
         \\ cmpwi cr7, 3, 0
         \\
-        \\ # if not 0, jump to end
-        \\ bne cr7, 2f
+        \\ # if not 0, restore stack and return
+        \\ beq cr7, 2f
+        \\ lwz 29, 0(1)
+        \\ lwz 30, 4(1)
+        \\ addi 1, 1, 16
+        \\ blr
         \\
         \\ #else: we're the child
+        \\ 2:
+        \\ .cfi_undefined lr
+        \\ li 31, 0
+        \\ mtlr 0
+        \\
         \\ #call funcptr: move arg (d) into r3
-        \\ mr 3, 31
-        \\ #move r30 (funcptr) into CTR reg
-        \\ mtctr 30
+        \\ mr 3, 30
+        \\ #move r29 (funcptr) into CTR reg
+        \\ mtctr 29
         \\ # call CTR reg
         \\ bctrl
         \\ # mov SYS_exit into r0 (the exit param is already in r3)
         \\ li 0, 1
         \\ sc
-        \\
-        \\ 2:
-        \\
-        \\ # restore stack
-        \\ lwz 30, 0(1)
-        \\ lwz 31, 4(1)
-        \\ addi 1, 1, 16
-        \\
-        \\ blr
     );
 }
 
lib/std/os/linux/powerpc64.zig
@@ -160,7 +160,12 @@ pub fn clone() callconv(.Naked) usize {
         \\  cmpwi cr7, 3, 0
         \\  bnelr cr7
         \\
-        \\  # we're the child. call fn(arg)
+        \\  # we're the child
+        \\  .cfi_undefined lr
+        \\  li    31, 0
+        \\  mtlr   0
+        \\
+        \\  # call fn(arg)
         \\  ld     3, 16(1)
         \\  ld    12,  8(1)
         \\  mtctr 12
lib/std/os/linux/riscv32.zig
@@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
         \\    ret
         \\
         \\    # Child
-        \\1:  lw a1, 0(sp)
+        \\1:  .cfi_undefined ra
+        \\    mv fp, zero
+        \\    mv ra, zero
+        \\
+        \\    lw a1, 0(sp)
         \\    lw a0, 4(sp)
         \\    jalr a1
         \\
lib/std/os/linux/riscv64.zig
@@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
         \\    ret
         \\
         \\    # Child
-        \\1:  ld a1, 0(sp)
+        \\1:  .cfi_undefined ra
+        \\    mv fp, zero
+        \\    mv ra, zero
+        \\
+        \\    ld a1, 0(sp)
         \\    ld a0, 8(sp)
         \\    jalr a1
         \\
lib/std/os/linux/s390x.zig
@@ -133,7 +133,12 @@ pub fn clone() callconv(.Naked) usize {
         \\ltgr %%r2, %%r2
         \\bnzr %%r14
         \\
-        \\# we're the child. call fn(arg)
+        \\# we're the child
+        \\.cfi_undefined %%r14
+        \\lghi %%r11, 0
+        \\lghi %%r14, 0
+        \\
+        \\# call fn(arg)
         \\lg   %%r1,  8(%%r15)
         \\lg   %%r2, 16(%%r15)
         \\basr %%r14, %%r1
lib/std/os/linux/sparc64.zig
@@ -198,29 +198,34 @@ pub fn clone() callconv(.Naked) usize {
         \\ mov %%i5, %%o3
         \\ ldx [%%fp + 0x8af], %%o4
         \\ t 0x6d
-        \\ bcs,pn %%xcc, 2f
+        \\ bcs,pn %%xcc, 1f
         \\ nop
         \\ # The child pid is returned in o0 while o1 tells if this
         \\ # process is # the child (=1) or the parent (=0).
-        \\ brnz %%o1, 1f
+        \\ brnz %%o1, 2f
         \\ nop
         \\ # Parent process, return the child pid
         \\ mov %%o0, %%i0
         \\ ret
         \\ restore
         \\1:
-        \\ # Child process, call func(arg)
+        \\ # The syscall failed
+        \\ sub %%g0, %%o0, %%i0
+        \\ ret
+        \\ restore
+        \\2:
+        \\ # Child process
+        \\ .cfi_undefined %%i7
+        \\ mov %%g0, %%fp
+        \\ mov %%g0, %%i7
+        \\
+        \\ # call func(arg)
         \\ mov %%g0, %%fp
         \\ call %%g2
         \\ mov %%g3, %%o0
         \\ # Exit
         \\ mov 1, %%g1 // SYS_exit
         \\ t 0x6d
-        \\2:
-        \\ # The syscall failed
-        \\ sub %%g0, %%o0, %%i0
-        \\ ret
-        \\ restore
     );
 }
 
lib/std/os/linux/x86.zig
@@ -148,19 +148,22 @@ pub fn clone() callconv(.Naked) usize {
         \\  movl $120,%%eax // SYS_clone
         \\  int $128
         \\  testl %%eax,%%eax
-        \\  jnz 1f
-        \\  popl %%eax
-        \\  xorl %%ebp,%%ebp
-        \\  calll *%%eax
-        \\  movl %%eax,%%ebx
-        \\  movl $1,%%eax // SYS_exit
-        \\  int $128
-        \\1:
+        \\  jz 1f
         \\  popl %%edi
         \\  popl %%esi
         \\  popl %%ebx
         \\  popl %%ebp
         \\  retl
+        \\
+        \\1:
+        \\  .cfi_undefined %%eip
+        \\  xorl %%ebp,%%ebp
+        \\
+        \\  popl %%eax
+        \\  calll *%%eax
+        \\  movl %%eax,%%ebx
+        \\  movl $1,%%eax // SYS_exit
+        \\  int $128
     );
 }
 
lib/std/os/linux/x86_64.zig
@@ -116,8 +116,10 @@ pub fn clone() callconv(.Naked) usize {
         \\      testq %%rax,%%rax
         \\      jz 1f
         \\      retq
+        \\
         \\1:    .cfi_undefined %%rip
         \\      xorl %%ebp,%%ebp
+        \\
         \\      popq %%rdi
         \\      callq *%%r9
         \\      movl %%eax,%%edi