aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/signal_unix.go
diff options
context:
space:
mode:
authorYuichi Nishiwaki <yuichi.nishiwaki@gmail.com>2019-09-11 02:26:02 +0000
committerIan Lance Taylor <iant@golang.org>2019-09-11 03:32:35 +0000
commit904f046e2ba812e04230c6e5252b3ca87c41e0e1 (patch)
tree1fa79400ee7e4c2ef7916bff96f094358ee6bc29 /src/runtime/signal_unix.go
parent8ef6d6a8f24354ef167f9dca54ab64e1ea6579f0 (diff)
downloadgo-904f046e2ba812e04230c6e5252b3ca87c41e0e1.tar.gz
go-904f046e2ba812e04230c6e5252b3ca87c41e0e1.zip
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: 28ce42c4a02a060f08c1b0dd1c9a392123fd2ee9 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>
Diffstat (limited to 'src/runtime/signal_unix.go')
-rw-r--r--src/runtime/signal_unix.go27
1 files changed, 21 insertions, 6 deletions
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 436c18c126..c9f57a7ba4 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -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
}