]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix bad signal stack when using cgo-created threads and async signals
authorRuss Cox <rsc@golang.org>
Fri, 13 Nov 2015 21:21:01 +0000 (16:21 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 18 Nov 2015 18:05:22 +0000 (18:05 +0000)
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>

src/runtime/os1_darwin.go
src/runtime/os1_dragonfly.go
src/runtime/os1_freebsd.go
src/runtime/os1_linux.go
src/runtime/os1_nacl.go
src/runtime/os1_netbsd.go
src/runtime/os1_openbsd.go
src/runtime/os1_plan9.go
src/runtime/os1_windows.go
src/runtime/os3_solaris.go
src/runtime/proc.go

index ba38a78ed1974dff4e022da9eed66e8fd6f90628..fd5637f953a8ff0e803e1d06df9c52f9ae3c60fd 100644 (file)
@@ -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 {
index a1be981f56b88c4fc9417d683767df263cab1703..3f17da279ea814fca042c2c8e0fd6420ae55b15d 100644 (file)
@@ -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 {
index a325620fe6d02cd79af47d4a9f4889e26ae784bd..7aa705ed0660415cc24ab3817a07b912c0886066 100644 (file)
@@ -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 {
index 8b5cdd346557daef0ac631cfbea3295a28e9cb09..cb73500a2f794c25a8fd1e36111d3946052233e0 100644 (file)
@@ -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 {
index ad4329cecd12ddd39fed8627c329948a0c1f55f9..c2ceb1724ee12c67c692bf2d4fb7a8fe98277a48 100644 (file)
@@ -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() {
index 3e77d248f79bfa43dedf5620b9a7341ee1862e3d..767c535eecca77ffcb4b44a2afcd9a898dfde74b 100644 (file)
@@ -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 {
index 11034a64f664f07dba1494274589ff0daa80c19d..0cfb1348afecd913e4cb3dba4aa960a25959fab7 100644 (file)
@@ -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 {
index bc7ce65dafd18c02e59971c8a43360e69e4e3479..70cd158470ad6d81845d311ee85aac7f51504668 100644 (file)
@@ -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() {
index 813454357824e8537c221132cca8d952777bb2c9..551705797d935b51bace25669c274d7745a642f1 100644 (file)
@@ -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)
index 3ac121a7b828debe83d779c63a8d3c6b2649e061..ad697487b00061363a276952e4bd0d4e3c5e7eec 100644 (file)
@@ -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)))
 }
index a98d138f35470aee7f1e6fdda5973f756dc2f2c8..aaabfae7b864963216b38b3c4a68593b0edadd73 100644 (file)
@@ -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)
 }