1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:04:44 -07:00
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.
This commit is contained in:
Yuichi Nishiwaki 2019-09-05 06:22:23 +01:00
parent 5d04e76409
commit 28ce42c4a0
4 changed files with 25 additions and 8 deletions

View File

@ -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 {

View File

@ -274,6 +274,21 @@ func sigpipe() {
dieFromSignal(_SIGPIPE)
}
// sigFetchG fetches the value of G safely when running in a signal handler.
// On some architectures, the g value may be clobbered when running in a VDSO.
// See issue #32912.
//
//go:nosplit
func sigFetchG(c *sigctxt) *g {
switch GOARCH {
case "arm", "arm64", "ppc64", "ppc64le":
if inVDSOPage(c.sigpc()) {
return nil
}
}
return getg()
}
// sigtrampgo is called from the signal handler function, sigtramp,
// written in assembly code.
// 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) {
if sigfwdgo(sig, info, ctx) {
return
}
g := getg()
c := &sigctxt{info, ctx}
g := sigFetchG(c)
if g == nil {
c := &sigctxt{info, ctx}
if sig == _SIGPROF {
sigprofNonGoPC(c.sigpc())
return
@ -347,7 +362,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,9 +671,10 @@ 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.
g := getg()
// (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.
g := sigFetchG(c)
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
return false
}

View File

@ -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()
}

View File

@ -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 {