Skip to content

Commit 28ce42c

Browse files
committed
fixes #32912
The crash occurs when go runtime calls a VDSO function (say __vdso_clock_gettime) and a signal arrives to that thread. Since VDSO functions temporarily destroy the G register (R10), Go functions asynchronously executed in that thread (i.e. Go's signal handler) can try to load data from the destroyed G, which causes segmentation fault.
1 parent 5d04e76 commit 28ce42c

File tree

4 files changed

+25
-8
lines changed

4 files changed

+25
-8
lines changed

src/runtime/crash_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error)
144144
}
145145

146146
func TestVDSO(t *testing.T) {
147+
t.Parallel()
147148
output := runTestProg(t, "testprog", "SignalInVDSO")
148149
want := "success\n"
149150
if output != want {

src/runtime/signal_unix.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,21 @@ func sigpipe() {
274274
dieFromSignal(_SIGPIPE)
275275
}
276276

277+
// sigFetchG fetches the value of G safely when running in a signal handler.
278+
// On some architectures, the g value may be clobbered when running in a VDSO.
279+
// See issue #32912.
280+
//
281+
//go:nosplit
282+
func sigFetchG(c *sigctxt) *g {
283+
switch GOARCH {
284+
case "arm", "arm64", "ppc64", "ppc64le":
285+
if inVDSOPage(c.sigpc()) {
286+
return nil
287+
}
288+
}
289+
return getg()
290+
}
291+
277292
// sigtrampgo is called from the signal handler function, sigtramp,
278293
// written in assembly code.
279294
// This is called by the signal handler, and the world may be stopped.
@@ -289,9 +304,9 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
289304
if sigfwdgo(sig, info, ctx) {
290305
return
291306
}
292-
g := getg()
307+
c := &sigctxt{info, ctx}
308+
g := sigFetchG(c)
293309
if g == nil {
294-
c := &sigctxt{info, ctx}
295310
if sig == _SIGPROF {
296311
sigprofNonGoPC(c.sigpc())
297312
return
@@ -347,7 +362,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
347362
signalDuringFork(sig)
348363
}
349364

350-
c := &sigctxt{info, ctx}
351365
c.fixsigcode(sig)
352366
sighandler(sig, info, ctx, g)
353367
setg(g)
@@ -657,9 +671,10 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
657671
return false
658672
}
659673
// Determine if the signal occurred inside Go code. We test that:
660-
// (1) we were in a goroutine (i.e., m.curg != nil), and
661-
// (2) we weren't in CGO.
662-
g := getg()
674+
// (1) we weren't in VDSO page,
675+
// (2) we were in a goroutine (i.e., m.curg != nil), and
676+
// (3) we weren't in CGO.
677+
g := sigFetchG(c)
663678
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
664679
return false
665680
}

src/runtime/testdata/testprog/vdso.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ func signalInVDSO() {
3333
t0 := time.Now()
3434
t1 := t0
3535
// We should get a profiling signal 100 times a second,
36-
// so running for 10 seconds should be sufficient.
37-
for t1.Sub(t0) < 10*time.Second {
36+
// so running for 1 second should be sufficient.
37+
for t1.Sub(t0) < time.Second {
3838
t1 = time.Now()
3939
}
4040

src/runtime/vdso_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ func vdsoauxv(tag, val uintptr) {
281281
}
282282

283283
// vdsoMarker reports whether PC is on the VDSO page.
284+
//go:nosplit
284285
func inVDSOPage(pc uintptr) bool {
285286
for _, k := range vdsoSymbolKeys {
286287
if *k.ptr != 0 {

0 commit comments

Comments
 (0)