mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: fix crash during VDSO calls on arm
As discussed in #32912, a 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.
To fix the issue a guard is inserted in front of sigtrampgo, so that the control escapes from
signal handlers without touching G in case the signal occurred in the VDSO context.
The test case included in the patch is take from discussion in a relevant thread on github:
https://github.com/golang/go/issues/32912#issuecomment-517874531.
This patch not only fixes the issue on AArch64 but also that on 32bit ARM.
Fixes #32912
Change-Id: I657472e54b7aa3c617fabc5019ce63aa4105624a
GitHub-Last-Rev: 28ce42c4a0
GitHub-Pull-Request: golang/go#34030
Reviewed-on: https://go-review.googlesource.com/c/go/+/192937
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
8ef6d6a8f2
commit
904f046e2b
4 changed files with 86 additions and 6 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue