From f8e6418637c8163d9046ccf700d3486000fddb26 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 13 Nov 2015 16:21:01 -0500 Subject: [PATCH] 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 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot --- src/runtime/os1_darwin.go | 17 ++++++++++++++--- src/runtime/os1_dragonfly.go | 16 +++++++++++++--- src/runtime/os1_freebsd.go | 17 ++++++++++++++--- src/runtime/os1_linux.go | 17 ++++++++++++++--- src/runtime/os1_nacl.go | 9 +++++++++ src/runtime/os1_netbsd.go | 18 ++++++++++++++---- src/runtime/os1_openbsd.go | 17 ++++++++++++++--- src/runtime/os1_plan9.go | 6 ++++++ src/runtime/os1_windows.go | 10 ++++++++++ src/runtime/os3_solaris.go | 18 ++++++++++++++---- src/runtime/proc.go | 22 ++++++++++++++++++---- 11 files changed, 140 insertions(+), 27 deletions(-) diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go index ba38a78ed1..fd5637f953 100644 --- a/src/runtime/os1_darwin.go +++ b/src/runtime/os1_darwin.go @@ -130,6 +130,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { } // 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 @@ func getsig(i int32) uintptr { 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 a1be981f56..3f17da279e 100644 --- a/src/runtime/os1_dragonfly.go +++ b/src/runtime/os1_dragonfly.go @@ -117,6 +117,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { // 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 @@ func getsig(i int32) uintptr { 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 a325620fe6..7aa705ed06 100644 --- a/src/runtime/os1_freebsd.go +++ b/src/runtime/os1_freebsd.go @@ -120,6 +120,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { } // 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 @@ func getsig(i int32) uintptr { 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 8b5cdd3465..cb73500a2f 100644 --- a/src/runtime/os1_linux.go +++ b/src/runtime/os1_linux.go @@ -197,6 +197,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { } // 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 @@ func getsig(i int32) uintptr { 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 ad4329cecd..c2ceb1724e 100644 --- a/src/runtime/os1_nacl.go +++ b/src/runtime/os1_nacl.go @@ -15,9 +15,18 @@ func mpreinit(mp *m) { 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 3e77d248f7..767c535eec 100644 --- a/src/runtime/os1_netbsd.go +++ b/src/runtime/os1_netbsd.go @@ -138,6 +138,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { } // 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 @@ func getsig(i int32) uintptr { 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 11034a64f6..0cfb1348af 100644 --- a/src/runtime/os1_openbsd.go +++ b/src/runtime/os1_openbsd.go @@ -148,6 +148,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { *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 @@ func minit() { } // 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 @@ func getsig(i int32) uintptr { 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 bc7ce65daf..70cd158470 100644 --- a/src/runtime/os1_plan9.go +++ b/src/runtime/os1_plan9.go @@ -24,6 +24,12 @@ func mpreinit(mp *m) { 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 8134543578..551705797d 100644 --- a/src/runtime/os1_windows.go +++ b/src/runtime/os1_windows.go @@ -390,9 +390,18 @@ func newosproc(mp *m, stk unsafe.Pointer) { 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 @@ func minit() { } // 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 3ac121a7b8..ad697487b0 100644 --- a/src/runtime/os3_solaris.go +++ b/src/runtime/os3_solaris.go @@ -192,6 +192,7 @@ func mpreinit(mp *m) { 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 @@ func msigsave(mp *m) { 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 @@ func minit() { // 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 @@ func getsig(i int32) uintptr { return *((*uintptr)(unsafe.Pointer(&sa._funcptr))) } +//go:nosplit func signalstack(s *stack) { var st sigaltstackt if s == nil { @@ -497,6 +506,7 @@ func sigaltstack(ss *sigaltstackt, oss *sigaltstackt) /* int32 */ { 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 a98d138f35..aaabfae7b8 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1276,6 +1276,15 @@ func needm(x byte) { 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 @@ func needm(x byte) { _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 @@ func newextram() { // 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 @@ func dropm() { 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) }