authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-11-04 18:30:36+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-12-11 00:10:17+01:00
logda018f972619377e8c044c5c27ebf231be262bb3
tree00faa647aca74f648677b74828301ab0f8c14fc0
parent7d0087707634028308d3a870883249bfa59d94b8
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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

14 files changed, 98 insertions(+), 44 deletions(-)

lib/std/os/linux/aarch64.zig+6-1
......@@ -120,8 +120,13 @@ pub fn clone() callconv(.Naked) usize {
120120 \\ cbz x0,1f
121121 \\ // parent
122122 \\ ret
123 \\
123124 \\ // child
124 \\1: ldp x1,x0,[sp],#16
125 \\1: .cfi_undefined lr
126 \\ mov fp, 0
127 \\ mov lr, 0
128 \\
129 \\ ldp x1,x0,[sp],#16
125130 \\ blr x1
126131 \\ mov x8,#93 // SYS_exit
127132 \\ svc #0
lib/std/os/linux/arm.zig+8-3
......@@ -120,11 +120,16 @@ pub fn clone() callconv(.Naked) usize {
120120 \\ ldmfd sp!,{r4,r5,r6,r7}
121121 \\ bx lr
122122 \\
123 \\1: mov r0,r6
123 \\ // https://github.com/llvm/llvm-project/issues/115891
124 \\1: mov r7, #0
125 \\ mov r11, #0
126 \\ mov lr, #0
127 \\
128 \\ mov r0,r6
124129 \\ bl 3f
125 \\2: mov r7,#1 // SYS_exit
130 \\ mov r7,#1 // SYS_exit
126131 \\ svc 0
127 \\ b 2b
132 \\
128133 \\3: bx r5
129134 );
130135}
lib/std/os/linux/hexagon.zig+4
......@@ -118,6 +118,10 @@ pub fn clone() callconv(.Naked) usize {
118118 \\ p0 = cmp.eq(r0, #0)
119119 \\ if (!p0) dealloc_return
120120 \\
121 \\ .cfi_undefined r31
122 \\ r30 = #0
123 \\ r31 = #0
124 \\
121125 \\ r0 = r10
122126 \\ callr r11
123127 \\
lib/std/os/linux/loongarch64.zig+4
......@@ -121,6 +121,10 @@ pub fn clone() callconv(.Naked) usize {
121121 \\ beqz $a0, 1f # whether child process
122122 \\ jirl $zero, $ra, 0 # parent process return
123123 \\1:
124 \\ .cfi_undefined 1
125 \\ move $fp, $zero
126 \\ move $ra, $zero
127 \\
124128 \\ ld.d $t8, $sp, 0 # function pointer
125129 \\ ld.d $a0, $sp, 8 # argument pointer
126130 \\ jirl $ra, $t8, 0 # call the user's function
lib/std/os/linux/mips.zig+4
......@@ -231,6 +231,10 @@ pub fn clone() callconv(.Naked) usize {
231231 \\ jr $ra
232232 \\ nop
233233 \\1:
234 \\ .cfi_undefined $ra
235 \\ move $fp, $zero
236 \\ move $ra, $zero
237 \\
234238 \\ lw $25, 0($sp)
235239 \\ lw $4, 4($sp)
236240 \\ jalr $25
lib/std/os/linux/mips64.zig+4
......@@ -210,6 +210,10 @@ pub fn clone() callconv(.Naked) usize {
210210 \\ jr $ra
211211 \\ nop
212212 \\1:
213 \\ .cfi_undefined $ra
214 \\ move $fp, $zero
215 \\ move $ra, $zero
216 \\
213217 \\ ld $25, 0($sp)
214218 \\ ld $4, 8($sp)
215219 \\ jalr $25
lib/std/os/linux/powerpc.zig+20-20
......@@ -133,14 +133,14 @@ pub fn clone() callconv(.Naked) usize {
133133 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
134134 // 0 3, 4, 5, 6, 7
135135 asm volatile (
136 \\ # store non-volatile regs r30, r31 on stack in order to put our
136 \\ # store non-volatile regs r29, r30 on stack in order to put our
137137 \\ # start func and its arg there
138 \\ stwu 30, -16(1)
139 \\ stw 31, 4(1)
138 \\ stwu 29, -16(1)
139 \\ stw 30, 4(1)
140140 \\
141 \\ # save r3 (func) into r30, and r6(arg) into r31
142 \\ mr 30, 3
143 \\ mr 31, 6
141 \\ # save r3 (func) into r29, and r6(arg) into r30
142 \\ mr 29, 3
143 \\ mr 30, 6
144144 \\
145145 \\ # create initial stack frame for new thread
146146 \\ clrrwi 4, 4, 4
......@@ -167,28 +167,28 @@ pub fn clone() callconv(.Naked) usize {
167167 \\ # compare sc result with 0
168168 \\ cmpwi cr7, 3, 0
169169 \\
170 \\ # if not 0, jump to end
171 \\ bne cr7, 2f
170 \\ # if not 0, restore stack and return
171 \\ beq cr7, 2f
172 \\ lwz 29, 0(1)
173 \\ lwz 30, 4(1)
174 \\ addi 1, 1, 16
175 \\ blr
172176 \\
173177 \\ #else: we're the child
178 \\ 2:
179 \\ .cfi_undefined lr
180 \\ li 31, 0
181 \\ mtlr 0
182 \\
174183 \\ #call funcptr: move arg (d) into r3
175 \\ mr 3, 31
176 \\ #move r30 (funcptr) into CTR reg
177 \\ mtctr 30
184 \\ mr 3, 30
185 \\ #move r29 (funcptr) into CTR reg
186 \\ mtctr 29
178187 \\ # call CTR reg
179188 \\ bctrl
180189 \\ # mov SYS_exit into r0 (the exit param is already in r3)
181190 \\ li 0, 1
182191 \\ sc
183 \\
184 \\ 2:
185 \\
186 \\ # restore stack
187 \\ lwz 30, 0(1)
188 \\ lwz 31, 4(1)
189 \\ addi 1, 1, 16
190 \\
191 \\ blr
192192 );
193193}
194194
lib/std/os/linux/powerpc64.zig+6-1
......@@ -160,7 +160,12 @@ pub fn clone() callconv(.Naked) usize {
160160 \\ cmpwi cr7, 3, 0
161161 \\ bnelr cr7
162162 \\
163 \\ # we're the child. call fn(arg)
163 \\ # we're the child
164 \\ .cfi_undefined lr
165 \\ li 31, 0
166 \\ mtlr 0
167 \\
168 \\ # call fn(arg)
164169 \\ ld 3, 16(1)
165170 \\ ld 12, 8(1)
166171 \\ mtctr 12
lib/std/os/linux/riscv32.zig+5-1
......@@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
120120 \\ ret
121121 \\
122122 \\ # Child
123 \\1: lw a1, 0(sp)
123 \\1: .cfi_undefined ra
124 \\ mv fp, zero
125 \\ mv ra, zero
126 \\
127 \\ lw a1, 0(sp)
124128 \\ lw a0, 4(sp)
125129 \\ jalr a1
126130 \\
lib/std/os/linux/riscv64.zig+5-1
......@@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
120120 \\ ret
121121 \\
122122 \\ # Child
123 \\1: ld a1, 0(sp)
123 \\1: .cfi_undefined ra
124 \\ mv fp, zero
125 \\ mv ra, zero
126 \\
127 \\ ld a1, 0(sp)
124128 \\ ld a0, 8(sp)
125129 \\ jalr a1
126130 \\
lib/std/os/linux/s390x.zig+6-1
......@@ -133,7 +133,12 @@ pub fn clone() callconv(.Naked) usize {
133133 \\ltgr %%r2, %%r2
134134 \\bnzr %%r14
135135 \\
136 \\# we're the child. call fn(arg)
136 \\# we're the child
137 \\.cfi_undefined %%r14
138 \\lghi %%r11, 0
139 \\lghi %%r14, 0
140 \\
141 \\# call fn(arg)
137142 \\lg %%r1, 8(%%r15)
138143 \\lg %%r2, 16(%%r15)
139144 \\basr %%r14, %%r1
lib/std/os/linux/sparc64.zig+13-8
......@@ -198,29 +198,34 @@ pub fn clone() callconv(.Naked) usize {
198198 \\ mov %%i5, %%o3
199199 \\ ldx [%%fp + 0x8af], %%o4
200200 \\ t 0x6d
201 \\ bcs,pn %%xcc, 2f
201 \\ bcs,pn %%xcc, 1f
202202 \\ nop
203203 \\ # The child pid is returned in o0 while o1 tells if this
204204 \\ # process is # the child (=1) or the parent (=0).
205 \\ brnz %%o1, 1f
205 \\ brnz %%o1, 2f
206206 \\ nop
207207 \\ # Parent process, return the child pid
208208 \\ mov %%o0, %%i0
209209 \\ ret
210210 \\ restore
211211 \\1:
212 \\ # Child process, call func(arg)
212 \\ # The syscall failed
213 \\ sub %%g0, %%o0, %%i0
214 \\ ret
215 \\ restore
216 \\2:
217 \\ # Child process
218 \\ .cfi_undefined %%i7
219 \\ mov %%g0, %%fp
220 \\ mov %%g0, %%i7
221 \\
222 \\ # call func(arg)
213223 \\ mov %%g0, %%fp
214224 \\ call %%g2
215225 \\ mov %%g3, %%o0
216226 \\ # Exit
217227 \\ mov 1, %%g1 // SYS_exit
218228 \\ t 0x6d
219 \\2:
220 \\ # The syscall failed
221 \\ sub %%g0, %%o0, %%i0
222 \\ ret
223 \\ restore
224229 );
225230}
226231
lib/std/os/linux/x86.zig+11-8
......@@ -148,19 +148,22 @@ pub fn clone() callconv(.Naked) usize {
148148 \\ movl $120,%%eax // SYS_clone
149149 \\ int $128
150150 \\ testl %%eax,%%eax
151 \\ jnz 1f
152 \\ popl %%eax
153 \\ xorl %%ebp,%%ebp
154 \\ calll *%%eax
155 \\ movl %%eax,%%ebx
156 \\ movl $1,%%eax // SYS_exit
157 \\ int $128
158 \\1:
151 \\ jz 1f
159152 \\ popl %%edi
160153 \\ popl %%esi
161154 \\ popl %%ebx
162155 \\ popl %%ebp
163156 \\ retl
157 \\
158 \\1:
159 \\ .cfi_undefined %%eip
160 \\ xorl %%ebp,%%ebp
161 \\
162 \\ popl %%eax
163 \\ calll *%%eax
164 \\ movl %%eax,%%ebx
165 \\ movl $1,%%eax // SYS_exit
166 \\ int $128
164167 );
165168}
166169
lib/std/os/linux/x86_64.zig+2
......@@ -116,8 +116,10 @@ pub fn clone() callconv(.Naked) usize {
116116 \\ testq %%rax,%%rax
117117 \\ jz 1f
118118 \\ retq
119 \\
119120 \\1: .cfi_undefined %%rip
120121 \\ xorl %%ebp,%%ebp
122 \\
121123 \\ popq %%rdi
122124 \\ callq *%%r9
123125 \\ movl %%eax,%%edi