Skip to content

Commit 758eb02

Browse files
committed
runtime: save/fetch g register during VDSO on ARM and ARM64
On ARM and ARM64, during a VDSO call, the g register may be temporarily clobbered by the VDSO code. If a signal is received during the execution of VDSO code, we may not find a valid g reading the g register. In CL 192937, we conservatively assume g is nil. But this approach has a problem: we cannot handle the signal in this case. Further, if the signal is not a profiling signal, we'll call badsignal, which calls needm, which wants to get an extra m, but we don't have one in a non-cgo binary, which cuases the program to hang. This is even more of a problem with async preemption, where we will receive more signals than before. I ran into this problem while working on async preemption support on ARM64. In this CL, before making a VDSO call, we save the g on the gsignal stack. When we receive a signal, we will be running on the gsignal stack, so we can fetch the g from there and move on. We probably want to do the same for PPC64. Currently we rely on that the VDSO code doesn't actually clobber the g register, but this is not guaranteed and we don't have control with. Idea from discussion with Dan Cross and Austin. Should fix #34391. Change-Id: Idbefc5e4c2f4373192c2be797be0140ae08b26e3 Reviewed-on: https://go-review.googlesource.com/c/go/+/202759 Run-TryBot: Cherry Zhang <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 20bba86 commit 758eb02

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed

src/os/signal/signal_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,52 @@ func atomicStopTestProgram() {
469469

470470
os.Exit(0)
471471
}
472+
473+
func TestTime(t *testing.T) {
474+
// Test that signal works fine when we are in a call to get time,
475+
// which on some platforms is using VDSO. See issue #34391.
476+
dur := 3 * time.Second
477+
if testing.Short() {
478+
dur = 100 * time.Millisecond
479+
}
480+
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
481+
done := make(chan bool)
482+
finished := make(chan bool)
483+
go func() {
484+
sig := make(chan os.Signal, 1)
485+
Notify(sig, syscall.SIGUSR1)
486+
defer Stop(sig)
487+
Loop:
488+
for {
489+
select {
490+
case <-sig:
491+
case <-done:
492+
break Loop
493+
}
494+
}
495+
finished <- true
496+
}()
497+
go func() {
498+
Loop:
499+
for {
500+
select {
501+
case <-done:
502+
break Loop
503+
default:
504+
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
505+
runtime.Gosched()
506+
}
507+
}
508+
finished <- true
509+
}()
510+
t0 := time.Now()
511+
for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
512+
} // hammering on getting time
513+
close(done)
514+
<-finished
515+
<-finished
516+
// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
517+
// into subsequent TestSignal() causing failure.
518+
// Sleep for a while to reduce the possibility of the failure.
519+
time.Sleep(10 * time.Millisecond)
520+
}

src/runtime/proc.go

+3
Original file line numberDiff line numberDiff line change
@@ -3420,6 +3420,9 @@ func malg(stacksize int32) *g {
34203420
})
34213421
newg.stackguard0 = newg.stack.lo + _StackGuard
34223422
newg.stackguard1 = ^uintptr(0)
3423+
// Clear the bottom word of the stack. We record g
3424+
// there on gsignal stack during VDSO on ARM and ARM64.
3425+
*(*uintptr)(unsafe.Pointer(newg.stack.lo)) = 0
34233426
}
34243427
return newg
34253428
}

src/runtime/signal_unix.go

+10
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ func sigFetchG(c *sigctxt) *g {
299299
switch GOARCH {
300300
case "arm", "arm64":
301301
if inVDSOPage(c.sigpc()) {
302+
// Before making a VDSO call we save the g to the bottom of the
303+
// signal stack. Fetch from there.
304+
// TODO: in efence mode, stack is sysAlloc'd, so this wouldn't
305+
// work.
306+
sp := getcallersp()
307+
s := spanOf(sp)
308+
if s != nil && s.state == mSpanManual && s.base() < sp && sp < s.limit {
309+
gp := *(**g)(unsafe.Pointer(s.base()))
310+
return gp
311+
}
302312
return nil
303313
}
304314
}

src/runtime/sys_linux_arm.s

+32
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,23 @@ noswitch:
259259
CMP $0, R11
260260
B.EQ fallback
261261

262+
// Store g on gsignal's stack, so if we receive a signal
263+
// during VDSO code we can find the g.
264+
// If we don't have a signal stack, we won't receive signal,
265+
// so don't bother saving g.
266+
MOVW m_gsignal(R5), R6 // g.m.gsignal
267+
CMP $0, R6
268+
BEQ 3(PC)
269+
MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo
270+
MOVW g, (R6)
271+
262272
BL (R11)
273+
274+
CMP $0, R6 // R6 is unchanged by C code
275+
BEQ 3(PC)
276+
MOVW $0, R1
277+
MOVW R1, (R6) // clear g slot
278+
263279
JMP finish
264280

265281
fallback:
@@ -310,7 +326,23 @@ noswitch:
310326
CMP $0, R11
311327
B.EQ fallback
312328

329+
// Store g on gsignal's stack, so if we receive a signal
330+
// during VDSO code we can find the g.
331+
// If we don't have a signal stack, we won't receive signal,
332+
// so don't bother saving g.
333+
MOVW m_gsignal(R5), R6 // g.m.gsignal
334+
CMP $0, R6
335+
BEQ 3(PC)
336+
MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo
337+
MOVW g, (R6)
338+
313339
BL (R11)
340+
341+
CMP $0, R6 // R6 is unchanged by C code
342+
BEQ 3(PC)
343+
MOVW $0, R1
344+
MOVW R1, (R6) // clear g slot
345+
314346
JMP finish
315347

316348
fallback:

src/runtime/sys_linux_arm64.s

+28
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,21 @@ noswitch:
218218
MOVW $CLOCK_REALTIME, R0
219219
MOVD runtime·vdsoClockgettimeSym(SB), R2
220220
CBZ R2, fallback
221+
222+
// Store g on gsignal's stack, so if we receive a signal
223+
// during VDSO code we can find the g.
224+
// If we don't have a signal stack, we won't receive signal,
225+
// so don't bother saving g.
226+
MOVD m_gsignal(R21), R22 // g.m.gsignal
227+
CBZ R22, 3(PC)
228+
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
229+
MOVD g, (R22)
230+
221231
BL (R2)
232+
233+
CBZ R22, 2(PC) // R22 is unchanged by C code
234+
MOVD ZR, (R22) // clear g slot
235+
222236
B finish
223237

224238
fallback:
@@ -261,7 +275,21 @@ noswitch:
261275
MOVW $CLOCK_MONOTONIC, R0
262276
MOVD runtime·vdsoClockgettimeSym(SB), R2
263277
CBZ R2, fallback
278+
279+
// Store g on gsignal's stack, so if we receive a signal
280+
// during VDSO code we can find the g.
281+
// If we don't have a signal stack, we won't receive signal,
282+
// so don't bother saving g.
283+
MOVD m_gsignal(R21), R22 // g.m.gsignal
284+
CBZ R22, 3(PC)
285+
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
286+
MOVD g, (R22)
287+
264288
BL (R2)
289+
290+
CBZ R22, 2(PC) // R22 is unchanged by C code
291+
MOVD ZR, (R22) // clear g slot
292+
265293
B finish
266294

267295
fallback:

0 commit comments

Comments
 (0)