Skip to content

Commit 00b1081

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 00b1081

File tree

4 files changed

+24
-8
lines changed

4 files changed

+24
-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: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,20 @@ func sigpipe() {
274274
dieFromSignal(_SIGPIPE)
275275
}
276276

277+
// G may be clobbered in VDSO context in some architectures.
278+
// See #32912.
279+
//
280+
//go:nosplit
281+
func sigFetchG(c *sigctxt) *g {
282+
switch GOARCH {
283+
case "arm", "arm64", "ppc64", "ppc64le":
284+
if inVDSOPage(c.sigpc()) {
285+
return nil
286+
}
287+
}
288+
return getg()
289+
}
290+
277291
// sigtrampgo is called from the signal handler function, sigtramp,
278292
// written in assembly code.
279293
// This is called by the signal handler, and the world may be stopped.
@@ -289,9 +303,9 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
289303
if sigfwdgo(sig, info, ctx) {
290304
return
291305
}
292-
g := getg()
306+
c := &sigctxt{info, ctx}
307+
g := sigFetchG(c)
293308
if g == nil {
294-
c := &sigctxt{info, ctx}
295309
if sig == _SIGPROF {
296310
sigprofNonGoPC(c.sigpc())
297311
return
@@ -347,7 +361,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
347361
signalDuringFork(sig)
348362
}
349363

350-
c := &sigctxt{info, ctx}
351364
c.fixsigcode(sig)
352365
sighandler(sig, info, ctx, g)
353366
setg(g)
@@ -657,9 +670,10 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
657670
return false
658671
}
659672
// 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()
673+
// (1) we weren't in VDSO page,
674+
// (2) we were in a goroutine (i.e., m.curg != nil), and
675+
// (3) we weren't in CGO.
676+
g := sigFetchG(c)
663677
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
664678
return false
665679
}

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)