mirror of
https://github.com/golang/go
synced 2024-11-18 16:04:44 -07:00
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.
This commit is contained in:
parent
5d04e76409
commit
28ce42c4a0
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
4
src/runtime/testdata/testprog/vdso.go
vendored
4
src/runtime/testdata/testprog/vdso.go
vendored
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user