runtime: fix bad signal stack when using cgo-created threads and async signals

Cgo-created threads transition between having associated Go g's and m's and not.
A signal arriving during the transition could think it was safe and appropriate to
run Go signal handlers when it was in fact not.
Avoid the race by masking all signals during the transition.

Fixes #12277.

Change-Id: Ie9711bc1d098391d58362492197a7e0f5b497d14
Reviewed-on: https://go-review.googlesource.com/16915
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
index ba38a78..fd5637f 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -130,6 +130,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -138,6 +139,17 @@
 	sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -156,10 +168,8 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
-	_g_ := getg()
-	smask := (*uint32)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask, nil)
 	signalstack(nil)
 }
 
@@ -459,6 +469,7 @@
 	return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st stackt
 	if s == nil {
diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go
index a1be981..3f17da2 100644
--- a/src/runtime/os1_dragonfly.go
+++ b/src/runtime/os1_dragonfly.go
@@ -117,6 +117,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -125,6 +126,17 @@
 	sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -148,9 +160,6 @@
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
-	_g_ := getg()
-	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask, nil)
 	signalstack(nil)
 }
 
@@ -220,6 +229,7 @@
 	return sa.sa_sigaction
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st sigaltstackt
 	if s == nil {
diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go
index a325620..7aa705e 100644
--- a/src/runtime/os1_freebsd.go
+++ b/src/runtime/os1_freebsd.go
@@ -120,6 +120,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -128,6 +129,17 @@
 	sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -153,10 +165,8 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
-	_g_ := getg()
-	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask, nil)
 	signalstack(nil)
 }
 
@@ -226,6 +236,7 @@
 	return sa.sa_handler
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st stackt
 	if s == nil {
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
index 8b5cdd3..cb73500 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -197,6 +197,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -205,6 +206,17 @@
 	rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	rtsigprocmask(_SIG_SETMASK, smask, nil, int32(unsafe.Sizeof(*smask)))
+}
+
+//go:nosplit
+func sigblock() {
+	rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all)))
+}
+
 func gettid() uint32
 
 // Called to initialize a new m (including the bootstrap m).
@@ -228,10 +240,8 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
-	_g_ := getg()
-	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-	rtsigprocmask(_SIG_SETMASK, smask, nil, int32(unsafe.Sizeof(*smask)))
 	signalstack(nil)
 }
 
@@ -325,6 +335,7 @@
 	return sa.sa_handler
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st sigaltstackt
 	if s == nil {
diff --git a/src/runtime/os1_nacl.go b/src/runtime/os1_nacl.go
index ad4329c..c2ceb17 100644
--- a/src/runtime/os1_nacl.go
+++ b/src/runtime/os1_nacl.go
@@ -15,9 +15,18 @@
 
 func sigtramp()
 
+//go:nosplit
 func msigsave(mp *m) {
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+}
+
+//go:nosplit
+func sigblock() {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go
index 3e77d24..767c535 100644
--- a/src/runtime/os1_netbsd.go
+++ b/src/runtime/os1_netbsd.go
@@ -138,6 +138,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -146,6 +147,17 @@
 	sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -166,11 +178,8 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
-	_g_ := getg()
-	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask, nil)
-
 	signalstack(nil)
 }
 
@@ -213,6 +222,7 @@
 	return sa.sa_sigaction
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st sigaltstackt
 	if s == nil {
diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go
index 11034a6..0cfb134 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -148,6 +148,7 @@
 	mp.gsignal.m = mp
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -156,6 +157,17 @@
 	*smask = sigprocmask(_SIG_BLOCK, 0)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := *(*uint32)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, sigset_all)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -178,10 +190,8 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
-	_g_ := getg()
-	smask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask)
 	signalstack(nil)
 }
 
@@ -224,6 +234,7 @@
 	return sa.sa_sigaction
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st stackt
 	if s == nil {
diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go
index bc7ce65..70cd158 100644
--- a/src/runtime/os1_plan9.go
+++ b/src/runtime/os1_plan9.go
@@ -24,6 +24,12 @@
 func msigsave(mp *m) {
 }
 
+func msigrestore(mp *m) {
+}
+
+func sigblock() {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go
index 8134543..5517057 100644
--- a/src/runtime/os1_windows.go
+++ b/src/runtime/os1_windows.go
@@ -390,9 +390,18 @@
 func mpreinit(mp *m) {
 }
 
+//go:nosplit
 func msigsave(mp *m) {
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+}
+
+//go:nosplit
+func sigblock() {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -402,6 +411,7 @@
 }
 
 // Called from dropm to undo the effect of an minit.
+//go:nosplit
 func unminit() {
 	tp := &getg().m.thread
 	stdcall1(_CloseHandle, *tp)
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index 3ac121a..ad69748 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -192,6 +192,7 @@
 
 func miniterrno()
 
+//go:nosplit
 func msigsave(mp *m) {
 	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
 	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
@@ -200,6 +201,17 @@
 	sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
+//go:nosplit
+func msigrestore(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -220,10 +232,6 @@
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
-	_g_ := getg()
-	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-	sigprocmask(_SIG_SETMASK, smask, nil)
-
 	signalstack(nil)
 }
 
@@ -289,6 +297,7 @@
 	return *((*uintptr)(unsafe.Pointer(&sa._funcptr)))
 }
 
+//go:nosplit
 func signalstack(s *stack) {
 	var st sigaltstackt
 	if s == nil {
@@ -497,6 +506,7 @@
 	sysvicall2(&libc_sigaltstack, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(oss)))
 }
 
+//go:nosplit
 func sigprocmask(how int32, set *sigset, oset *sigset) /* int32 */ {
 	sysvicall3(&libc_sigprocmask, uintptr(how), uintptr(unsafe.Pointer(set)), uintptr(unsafe.Pointer(oset)))
 }
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index a98d138..aaabfae 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -1276,6 +1276,15 @@
 	mp.needextram = mp.schedlink == 0
 	unlockextra(mp.schedlink.ptr())
 
+	// Save and block signals before installing g.
+	// Once g is installed, any incoming signals will try to execute,
+	// but we won't have the sigaltstack settings and other data
+	// set up appropriately until the end of minit, which will
+	// unblock the signals. This is the same dance as when
+	// starting a new m to run Go code via newosproc.
+	msigsave(mp)
+	sigblock()
+
 	// Install g (= m->g0) and set the stack bounds
 	// to match the current stack. We don't actually know
 	// how big the stack is, like we don't know how big any
@@ -1287,7 +1296,6 @@
 	_g_.stack.lo = uintptr(noescape(unsafe.Pointer(&x))) - 32*1024
 	_g_.stackguard0 = _g_.stack.lo + _StackGuard
 
-	msigsave(mp)
 	// Initialize this thread to use the m.
 	asminit()
 	minit()
@@ -1359,9 +1367,6 @@
 // We may have to keep the current version on systems with cgo
 // but without pthreads, like Windows.
 func dropm() {
-	// Undo whatever initialization minit did during needm.
-	unminit()
-
 	// Clear m and g, and return m to the extra list.
 	// After the call to setg we can only call nosplit functions
 	// with no pointer manipulation.
@@ -1369,7 +1374,16 @@
 	mnext := lockextra(true)
 	mp.schedlink.set(mnext)
 
+	// Block signals before unminit.
+	// Unminit unregisters the signal handling stack (but needs g on some systems).
+	// Setg(nil) clears g, which is the signal handler's cue not to run Go handlers.
+	// It's important not to try to handle a signal between those two steps.
+	sigblock()
+	unminit()
 	setg(nil)
+	msigrestore(mp)
+
+	// Commit the release of mp.
 	unlockextra(mp)
 }