Skip to content

Commit 7921829

Browse files
derekparkercagedmantis
authored andcommitted
[release-branch.go1.15] runtime: fix crash during VDSO calls on PowerPC
This patch reinstates a fix for PowerPC with regard to making VDSO calls while receiving a signal, and subsequently crashing. The crash happens because certain VDSO calls can modify the r30 register, which is where g is stored. This change was reverted for PowerPC because r30 is supposed to be a non-volatile register. This is true, but that only makes a guarantee across function calls, but not "within" a function call. This patch was seemingly fine before because the Linux kernel still had hand rolled assembly VDSO function calls, however with a recent change to C function calls it seems the compiler used can generate instructions which temporarily clobber r30. This means that when we receive a signal during one of these calls the value of r30 will not be the g as the runtime expects, causing a segfault. You can see from this assembly dump how the register is clobbered during the call: (the following is from a 5.13rc2 kernel) ``` Dump of assembler code for function __cvdso_clock_gettime_data: 0x00007ffff7ff0700 <+0>: cmplwi r4,15 0x00007ffff7ff0704 <+4>: bgt 0x7ffff7ff07f0 <__cvdso_clock_gettime_data+240> 0x00007ffff7ff0708 <+8>: li r9,1 0x00007ffff7ff070c <+12>: slw r9,r9,r4 0x00007ffff7ff0710 <+16>: andi. r10,r9,2179 0x00007ffff7ff0714 <+20>: beq 0x7ffff7ff0810 <__cvdso_clock_gettime_data+272> 0x00007ffff7ff0718 <+24>: rldicr r10,r4,4,59 0x00007ffff7ff071c <+28>: lis r9,32767 0x00007ffff7ff0720 <+32>: std r30,-16(r1) 0x00007ffff7ff0724 <+36>: std r31,-8(r1) 0x00007ffff7ff0728 <+40>: add r6,r3,r10 0x00007ffff7ff072c <+44>: ori r4,r9,65535 0x00007ffff7ff0730 <+48>: lwz r8,0(r3) 0x00007ffff7ff0734 <+52>: andi. r9,r8,1 0x00007ffff7ff0738 <+56>: bne 0x7ffff7ff07d0 <__cvdso_clock_gettime_data+208> 0x00007ffff7ff073c <+60>: lwsync 0x00007ffff7ff0740 <+64>: mftb r30 <---- RIGHT HERE => 0x00007ffff7ff0744 <+68>: ld r12,40(r6) ``` What I believe is happening is that the kernel changed the PowerPC VDSO calls to use standard C calls instead of using hand rolled assembly. The hand rolled assembly calls never touched r30, so this change was safe to roll back. That does not seem to be the case anymore as on the 5.13rc2 kernel the compiler *is* generating assembly which modifies r30, making this change again unsafe and causing a crash when the program receives a signal during these calls (which will happen often due to async preempt). This change happened here: https://lwn.net/ml/linux-kernel/235e5571959cfa89ced081d7e838ed5ff38447d2.1601365870.git.christophe.leroy@csgroup.eu/. I realize this was reverted due to unexplained hangs in PowerPC builders, but I think we should reinstate this change and investigate those issues separately: f4ca3c1 Fixes #46857 Change-Id: Ib18d7bbfc80a1a9cb558f0098878d41081324b52 GitHub-Last-Rev: c3002bc GitHub-Pull-Request: #46767 Reviewed-on: https://go-review.googlesource.com/c/go/+/328110 Run-TryBot: Lynn Boger <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Trust: Lynn Boger <[email protected]> (cherry picked from commit 16e82be) Reviewed-on: https://go-review.googlesource.com/c/go/+/334411 Run-TryBot: Carlos Amedee <[email protected]>
1 parent 1b68e07 commit 7921829

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

src/runtime/signal_unix.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ func preemptM(mp *m) {
393393
//go:nosplit
394394
func sigFetchG(c *sigctxt) *g {
395395
switch GOARCH {
396-
case "arm", "arm64":
396+
case "arm", "arm64", "ppc64", "ppc64le":
397397
if !iscgo && inVDSOPage(c.sigpc()) {
398398
// When using cgo, we save the g on TLS and load it from there
399399
// in sigtramp. Just use that.

src/runtime/sys_linux_ppc64x.s

+73-13
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,45 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16-12
215215
MOVD (g_sched+gobuf_sp)(R7), R1 // Set SP to g0 stack
216216

217217
noswitch:
218-
SUB $16, R1 // Space for results
219-
RLDICR $0, R1, $59, R1 // Align for C code
218+
SUB $16, R1 // Space for results
219+
RLDICR $0, R1, $59, R1 // Align for C code
220220
MOVD R12, CTR
221221
MOVD R1, R4
222-
BL (CTR) // Call from VDSO
223-
MOVD $0, R0 // Restore R0
224-
MOVD 0(R1), R3 // sec
225-
MOVD 8(R1), R5 // nsec
226-
MOVD R15, R1 // Restore SP
222+
223+
// Store g on gsignal's stack, so if we receive a signal
224+
// during VDSO code we can find the g.
225+
// If we don't have a signal stack, we won't receive signal,
226+
// so don't bother saving g.
227+
// When using cgo, we already saved g on TLS, also don't save
228+
// g here.
229+
// Also don't save g if we are already on the signal stack.
230+
// We won't get a nested signal.
231+
MOVBZ runtime·iscgo(SB), R22
232+
CMP R22, $0
233+
BNE nosaveg
234+
MOVD m_gsignal(R21), R22 // g.m.gsignal
235+
CMP R22, $0
236+
BEQ nosaveg
237+
238+
CMP g, R22
239+
BEQ nosaveg
240+
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
241+
MOVD g, (R22)
242+
243+
BL (CTR) // Call from VDSO
244+
245+
MOVD $0, (R22) // clear g slot, R22 is unchanged by C code
246+
247+
JMP finish
248+
249+
nosaveg:
250+
BL (CTR) // Call from VDSO
251+
252+
finish:
253+
MOVD $0, R0 // Restore R0
254+
MOVD 0(R1), R3 // sec
255+
MOVD 8(R1), R5 // nsec
256+
MOVD R15, R1 // Restore SP
227257

228258
// Restore vdsoPC, vdsoSP
229259
// We don't worry about being signaled between the two stores.
@@ -235,7 +265,7 @@ noswitch:
235265
MOVD 32(R1), R6
236266
MOVD R6, m_vdsoPC(R21)
237267

238-
finish:
268+
return:
239269
MOVD R3, sec+0(FP)
240270
MOVW R5, nsec+8(FP)
241271
RET
@@ -246,7 +276,7 @@ fallback:
246276
SYSCALL $SYS_clock_gettime
247277
MOVD 32(R1), R3
248278
MOVD 40(R1), R5
249-
JMP finish
279+
JMP return
250280

251281
TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
252282
MOVD $1, R3 // CLOCK_MONOTONIC
@@ -282,7 +312,37 @@ noswitch:
282312
RLDICR $0, R1, $59, R1 // Align for C code
283313
MOVD R12, CTR
284314
MOVD R1, R4
285-
BL (CTR) // Call from VDSO
315+
316+
// Store g on gsignal's stack, so if we receive a signal
317+
// during VDSO code we can find the g.
318+
// If we don't have a signal stack, we won't receive signal,
319+
// so don't bother saving g.
320+
// When using cgo, we already saved g on TLS, also don't save
321+
// g here.
322+
// Also don't save g if we are already on the signal stack.
323+
// We won't get a nested signal.
324+
MOVBZ runtime·iscgo(SB), R22
325+
CMP R22, $0
326+
BNE nosaveg
327+
MOVD m_gsignal(R21), R22 // g.m.gsignal
328+
CMP R22, $0
329+
BEQ nosaveg
330+
331+
CMP g, R22
332+
BEQ nosaveg
333+
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
334+
MOVD g, (R22)
335+
336+
BL (CTR) // Call from VDSO
337+
338+
MOVD $0, (R22) // clear g slot, R22 is unchanged by C code
339+
340+
JMP finish
341+
342+
nosaveg:
343+
BL (CTR) // Call from VDSO
344+
345+
finish:
286346
MOVD $0, R0 // Restore R0
287347
MOVD 0(R1), R3 // sec
288348
MOVD 8(R1), R5 // nsec
@@ -298,7 +358,7 @@ noswitch:
298358
MOVD 32(R1), R6
299359
MOVD R6, m_vdsoPC(R21)
300360

301-
finish:
361+
return:
302362
// sec is in R3, nsec in R5
303363
// return nsec in R3
304364
MOVD $1000000000, R4
@@ -313,7 +373,7 @@ fallback:
313373
SYSCALL $SYS_clock_gettime
314374
MOVD 32(R1), R3
315375
MOVD 40(R1), R5
316-
JMP finish
376+
JMP return
317377

318378
TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
319379
MOVW how+0(FP), R3
@@ -366,7 +426,7 @@ TEXT sigtramp<>(SB),NOSPLIT,$64
366426
// this might be called in external code context,
367427
// where g is not set.
368428
MOVBZ runtime·iscgo(SB), R6
369-
CMP R6, $0
429+
CMP R6, $0
370430
BEQ 2(PC)
371431
BL runtime·load_g(SB)
372432

0 commit comments

Comments
 (0)