From dac30212c25aa96560cdeca2579f7fbe3aaf5331 Mon Sep 17 00:00:00 2001 From: Yuichi Nishiwaki Date: Thu, 5 Sep 2019 06:22:23 +0100 Subject: [PATCH] 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. --- src/runtime/crash_test.go | 1 + src/runtime/signal_unix.go | 16 +++++++++++----- src/runtime/testdata/testprog/vdso.go | 4 ++-- src/runtime/vdso_linux.go | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 857b7001ea7858..c2cab7c813bf55 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -144,6 +144,7 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) } func TestVDSO(t *testing.T) { + t.Parallel() output := runTestProg(t, "testprog", "SignalInVDSO") want := "success\n" if output != want { diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index 436c18c1261f54..3d64f96204e30a 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -289,9 +289,12 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { if sigfwdgo(sig, info, ctx) { return } - g := getg() + c := &sigctxt{info, ctx} + var g *g + if !inVDSOPage(c.sigpc()) { + g = getg() + } if g == nil { - c := &sigctxt{info, ctx} if sig == _SIGPROF { sigprofNonGoPC(c.sigpc()) return @@ -347,7 +350,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { signalDuringFork(sig) } - c := &sigctxt{info, ctx} c.fixsigcode(sig) sighandler(sig, info, ctx, g) setg(g) @@ -657,8 +659,12 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { return false } // Determine if the signal occurred inside Go code. We test that: - // (1) we were in a goroutine (i.e., m.curg != nil), and - // (2) we weren't in CGO. + // (1) we weren't in VDSO page, + // (2) we were in a goroutine (i.e., m.curg != nil), and + // (3) we weren't in CGO. + if inVDSOPage(c.sigpc()) { + return false + } g := getg() if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo { return false diff --git a/src/runtime/testdata/testprog/vdso.go b/src/runtime/testdata/testprog/vdso.go index c952a08d487247..6036f45bc83c1e 100644 --- a/src/runtime/testdata/testprog/vdso.go +++ b/src/runtime/testdata/testprog/vdso.go @@ -33,8 +33,8 @@ func signalInVDSO() { t0 := time.Now() t1 := t0 // We should get a profiling signal 100 times a second, - // so running for 10 seconds should be sufficient. - for t1.Sub(t0) < 10*time.Second { + // so running for 1 second should be sufficient. + for t1.Sub(t0) < time.Second { t1 = time.Now() } diff --git a/src/runtime/vdso_linux.go b/src/runtime/vdso_linux.go index 71ba4ce4161b66..8518276867e394 100644 --- a/src/runtime/vdso_linux.go +++ b/src/runtime/vdso_linux.go @@ -281,6 +281,7 @@ func vdsoauxv(tag, val uintptr) { } // vdsoMarker reports whether PC is on the VDSO page. +//go:nosplit func inVDSOPage(pc uintptr) bool { for _, k := range vdsoSymbolKeys { if *k.ptr != 0 {