]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: if we don't handle a signal on a non-Go thread, raise it
authorIan Lance Taylor <iant@golang.org>
Wed, 22 Jul 2015 05:34:48 +0000 (22:34 -0700)
committerIan Lance Taylor <iant@golang.org>
Wed, 22 Jul 2015 20:26:29 +0000 (20:26 +0000)
In the past badsignal would crash the program.  In
https://golang.org/cl/10757044 badsignal was changed to call sigsend,
to fix issue #3250.  The effect of this was that when a non-Go thread
received a signal, and os/signal.Notify was not being used to check
for occurrences of the signal, the signal was ignored.

This changes the code so that if os/signal.Notify is not being used,
then the signal handler is reset to what it was, and the signal is
raised again.  This lets non-Go threads handle the signal as they
wish.  In particular, it means that a segmentation violation in a
non-Go thread will ordinarily crash the process, as it should.

Fixes #10139.
Update #11794.

Change-Id: I2109444aaada9d963ad03b1d071ec667760515e5
Reviewed-on: https://go-review.googlesource.com/12503
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>

26 files changed:
src/runtime/crash_cgo_test.go
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/os2_dragonfly.go
src/runtime/os2_freebsd.go
src/runtime/os2_linux.go
src/runtime/os2_solaris.go
src/runtime/os3_solaris.go
src/runtime/os_darwin.go
src/runtime/os_dragonfly.go
src/runtime/os_freebsd.go
src/runtime/os_linux.go
src/runtime/signal1_unix.go
src/runtime/signal_windows.go
src/runtime/sigqueue.go
src/runtime/sys_dragonfly_amd64.s
src/runtime/sys_freebsd_386.s
src/runtime/sys_freebsd_amd64.s
src/runtime/sys_freebsd_arm.s
src/runtime/sys_linux_amd64.s

index 9ff95e6f4411eee7cf3e4a375faf2620676ef5bc..d2847b0d454ed36dc828692bb90ad169b58b5ead 100644 (file)
@@ -82,6 +82,19 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
        }
 }
 
+func TestCgoExternalThreadSignal(t *testing.T) {
+       // issue 10139
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Skipf("no pthreads on %s", runtime.GOOS)
+       }
+       got := executeTest(t, cgoExternalThreadSignalSource, nil)
+       want := "OK\n"
+       if got != want {
+               t.Fatalf("expected %q, but got %q", want, got)
+       }
+}
+
 func TestCgoDLLImports(t *testing.T) {
        // test issue 9356
        if runtime.GOOS != "windows" {
@@ -282,6 +295,60 @@ func main() {
 }
 `
 
+const cgoExternalThreadSignalSource = `
+package main
+
+/*
+#include <pthread.h>
+
+void **nullptr;
+
+void *crash(void *p) {
+       *nullptr = p;
+       return 0;
+}
+
+int start_crashing_thread(void) {
+       pthread_t tid;
+       return pthread_create(&tid, 0, crash, 0);
+}
+*/
+import "C"
+
+import (
+       "fmt"
+       "os"
+       "os/exec"
+       "time"
+)
+
+func main() {
+       if len(os.Args) > 1 && os.Args[1] == "crash" {
+               i := C.start_crashing_thread()
+               if i != 0 {
+                       fmt.Println("pthread_create failed:", i)
+                       // Exit with 0 because parent expects us to crash.
+                       return
+               }
+
+               // We should crash immediately, but give it plenty of
+               // time before failing (by exiting 0) in case we are
+               // running on a slow system.
+               time.Sleep(5 * time.Second)
+               return
+       }
+
+       out, err := exec.Command(os.Args[0], "crash").CombinedOutput()
+       if err == nil {
+               fmt.Println("C signal did not crash as expected\n")
+               fmt.Printf("%s\n", out)
+               os.Exit(1)
+       }
+
+       fmt.Println("OK")
+}
+`
+
 const cgoDLLImportsMainSource = `
 package main
 
index 06bc2c79a57cdc6790e986d835f7944c51b9775d..08ec611d431469c3960d6bfa30184aaf015e237c 100644 (file)
@@ -469,3 +469,8 @@ func signalstack(s *stack) {
 func updatesigmask(m sigmask) {
        sigprocmask(_SIG_SETMASK, &m[0], nil)
 }
+
+func unblocksig(sig int32) {
+       mask := uint32(1) << (uint32(sig) - 1)
+       sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
index a4c11d4fbff19f76a49a0aac9a668714a38aa102..f96c78ca80a52370916d0e9ef15b63ffc9bd9d59 100644 (file)
@@ -78,7 +78,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
        }
 
        var oset sigset
-       sigprocmask(&sigset_all, &oset)
+       sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
 
        params := lwpparams{
                start_func: funcPC(lwp_start),
@@ -91,7 +91,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
        mp.tls[0] = uintptr(mp.id) // XXX so 386 asm can find it
 
        lwp_create(&params)
-       sigprocmask(&oset, nil)
+       sigprocmask(_SIG_SETMASK, &oset, nil)
 }
 
 func osinit() {
@@ -124,7 +124,7 @@ func msigsave(mp *m) {
        if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
                throw("insufficient storage for signal mask")
        }
-       sigprocmask(nil, smask)
+       sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
 // Called to initialize a new m (including the bootstrap m).
@@ -145,14 +145,14 @@ func minit() {
                        nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
                }
        }
-       sigprocmask(&nmask, nil)
+       sigprocmask(_SIG_SETMASK, &nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
        _g_ := getg()
        smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-       sigprocmask(smask, nil)
+       sigprocmask(_SIG_SETMASK, smask, nil)
        signalstack(nil)
 }
 
@@ -237,5 +237,11 @@ func signalstack(s *stack) {
 func updatesigmask(m sigmask) {
        var mask sigset
        copy(mask.__bits[:], m[:])
-       sigprocmask(&mask, nil)
+       sigprocmask(_SIG_SETMASK, &mask, nil)
+}
+
+func unblocksig(sig int32) {
+       var mask sigset
+       mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+       sigprocmask(_SIG_UNBLOCK, &mask, nil)
 }
index 6dbf8299b4d2509a40702c10043969edc7ddf72c..f3519f3490fdd786449be57cc787a69eae2a0b28 100644 (file)
@@ -88,9 +88,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
        mp.tls[0] = uintptr(mp.id) // so 386 asm can find it
 
        var oset sigset
-       sigprocmask(&sigset_all, &oset)
+       sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
        thr_new(&param, int32(unsafe.Sizeof(param)))
-       sigprocmask(&oset, nil)
+       sigprocmask(_SIG_SETMASK, &oset, nil)
 }
 
 func osinit() {
@@ -123,7 +123,7 @@ func msigsave(mp *m) {
        if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
                throw("insufficient storage for signal mask")
        }
-       sigprocmask(nil, smask)
+       sigprocmask(_SIG_SETMASK, nil, smask)
 }
 
 // Called to initialize a new m (including the bootstrap m).
@@ -147,14 +147,14 @@ func minit() {
                        nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
                }
        }
-       sigprocmask(&nmask, nil)
+       sigprocmask(_SIG_SETMASK, &nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
        _g_ := getg()
        smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
-       sigprocmask(smask, nil)
+       sigprocmask(_SIG_SETMASK, smask, nil)
        signalstack(nil)
 }
 
@@ -239,5 +239,11 @@ func signalstack(s *stack) {
 func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
        var mask sigset
        copy(mask.__bits[:], m[:])
-       sigprocmask(&mask, nil)
+       sigprocmask(_SIG_SETMASK, &mask, nil)
+}
+
+func unblocksig(sig int32) {
+       var mask sigset
+       mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+       sigprocmask(_SIG_UNBLOCK, &mask, nil)
 }
index e6942a9f79b69c730f7414f00c00ea51b7725899..dd64afca2caab351a63950057870b1953f6ae465 100644 (file)
@@ -333,3 +333,9 @@ func updatesigmask(m sigmask) {
        copy(mask[:], m[:])
        rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
 }
+
+func unblocksig(sig int32) {
+       var mask sigset
+       mask[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+       rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
+}
index 66e60f8b1222205d301860687d6d381fdb51949f..143752ada808b10fab40ddd91bd748d81d749057 100644 (file)
@@ -170,6 +170,10 @@ func badsignal2() {
 
 var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n")
 
+func raisebadsignal(sig int32) {
+       badsignal2()
+}
+
 func madvise(addr unsafe.Pointer, n uintptr, flags int32) {}
 func munmap(addr unsafe.Pointer, n uintptr)               {}
 func resetcpuprofiler(hz int32)                           {}
index 2a579b8694289cc29d5131d3bc23f320ce738d66..cacd60620b06a6f0bb320f7acb53570c7c0d7d2a 100644 (file)
@@ -230,3 +230,9 @@ func updatesigmask(m sigmask) {
        copy(mask.__bits[:], m[:])
        sigprocmask(_SIG_SETMASK, &mask, nil)
 }
+
+func unblocksig(sig int32) {
+       var mask sigset
+       mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+       sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
index c07cd243be53fcd4378258612a24bed9e4c6dd26..24a095b9d682e80d1120c113a012cde45702d7dd 100644 (file)
@@ -239,3 +239,8 @@ func signalstack(s *stack) {
 func updatesigmask(m sigmask) {
        sigprocmask(_SIG_SETMASK, m[0])
 }
+
+func unblocksig(sig int32) {
+       mask := uint32(1) << (uint32(sig) - 1)
+       sigprocmask(_SIG_UNBLOCK, mask)
+}
index bda7057f4493481f73b79e226df437c3691aec00..9615b6d1a40dadf938cd8f2a4ab9046d614532f6 100644 (file)
@@ -254,6 +254,10 @@ func badsignal2() {
        exits(&_badsignal[0])
 }
 
+func raisebadsignal(sig int32) {
+       badsignal2()
+}
+
 func _atoi(b []byte) int {
        n := 0
        for len(b) > 0 && '0' <= b[0] && b[0] <= '9' {
index 0a20ed43fab88c5f3bc892acbc1721ec9967a291..ccad82f013c453bab09755ec86e6315913fc6626 100644 (file)
@@ -5,8 +5,11 @@
 package runtime
 
 const (
-       _NSIG       = 33
-       _SI_USER    = 0x10001
-       _SS_DISABLE = 4
-       _RLIMIT_AS  = 10
+       _NSIG        = 33
+       _SI_USER     = 0x10001
+       _SS_DISABLE  = 4
+       _RLIMIT_AS   = 10
+       _SIG_BLOCK   = 1
+       _SIG_UNBLOCK = 2
+       _SIG_SETMASK = 3
 )
index f67211fdf2b8e1e6b549f499a3691dd0631e3556..84ab71523776a6434f5faef520a7e16f7e1bb0b2 100644 (file)
@@ -5,8 +5,11 @@
 package runtime
 
 const (
-       _SS_DISABLE = 4
-       _NSIG       = 33
-       _SI_USER    = 0x10001
-       _RLIMIT_AS  = 10
+       _SS_DISABLE  = 4
+       _NSIG        = 33
+       _SI_USER     = 0x10001
+       _RLIMIT_AS   = 10
+       _SIG_BLOCK   = 1
+       _SIG_UNBLOCK = 2
+       _SIG_SETMASK = 3
 )
index eaa9f0e833178162db28fb027f1731a9b1ebdbfa..71f36ebeff4c4b3af0ad25792ded328a937d1ca9 100644 (file)
@@ -8,6 +8,8 @@ const (
        _SS_DISABLE  = 2
        _NSIG        = 65
        _SI_USER     = 0
+       _SIG_BLOCK   = 0
+       _SIG_UNBLOCK = 1
        _SIG_SETMASK = 2
        _RLIMIT_AS   = 9
 )
index 26ca15f628d9303a51729719474e8c64101f1e41..f5c0c83316bcfdd43ae221db7092c401462811f0 100644 (file)
@@ -6,6 +6,7 @@ package runtime
 
 const (
        _SS_DISABLE  = 2
+       _SIG_UNBLOCK = 2
        _SIG_SETMASK = 3
        _NSIG        = 73 /* number of signals in sigtable array */
        _SI_USER     = 0
index 53d7b96b0f3100c5afd49b51e22efe1dc5b32433..7caa72e3beef4d2568c52a0cf8ab8a5889949749 100644 (file)
@@ -304,6 +304,12 @@ func updatesigmask(m sigmask) {
        sigprocmask(_SIG_SETMASK, &mask, nil)
 }
 
+func unblocksig(sig int32) {
+       var mask sigset
+       mask.__sigbits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
+       sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
+
 //go:nosplit
 func semacreate() uintptr {
        var sem *semt
index c432c99208b90e2b8f3f3a3a2855128376f6f44d..3deafd5227ec6bb418c7a3abe6bcae7f7a80abca 100644 (file)
@@ -20,7 +20,7 @@ func mach_thread_self() uint32
 func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
 
 //go:noescape
-func sigprocmask(sig uint32, new, old *uint32)
+func sigprocmask(how uint32, new, old *uint32)
 
 //go:noescape
 func sigaction(mode uint32, new, old *sigactiont)
index 60234bbdea60d17df9e1d966326ac183be689146..b19270a18dbd9f07957c91636bf073fd628c4177 100644 (file)
@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
 func sigaction(sig int32, new, old *sigactiont)
 
 //go:noescape
-func sigprocmask(new, old *sigset)
+func sigprocmask(how int32, new, old *sigset)
 
 //go:noescape
 func setitimer(mode int32, new, old *itimerval)
index b2b5cd1f3f459758a68eda94f7d3a2d3e2b034b3..8c8a10661d584b3970433a106b238432f4454a48 100644 (file)
@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
 func sigaction(sig int32, new, old *sigactiont)
 
 //go:noescape
-func sigprocmask(new, old *sigset)
+func sigprocmask(how int32, new, old *sigset)
 
 //go:noescape
 func setitimer(mode int32, new, old *itimerval)
index 523d28b2106f73fbef805e93c724d7b00887a8f6..bd492f5e3bbd423671e43541a2c381da76130dad 100644 (file)
@@ -29,8 +29,8 @@ func rtsigprocmask(sig uint32, new, old *sigset, size int32)
 
 //go:noescape
 func getrlimit(kind int32, limit unsafe.Pointer) int32
-func raise(sig uint32)
-func raiseproc(sig uint32)
+func raise(sig int32)
+func raiseproc(sig int32)
 
 //go:noescape
 func sched_getaffinity(pid, len uintptr, buf *uintptr) int32
index d3e9dac09797762eea5832892bc7cead4b82a5f6..56d975519022f1d411f167e45156f5ad962e34da 100644 (file)
@@ -140,6 +140,43 @@ func sigpipe() {
        raise(_SIGPIPE)
 }
 
+// raisebadsignal is called when a signal is received on a non-Go
+// thread, and the Go program does not want to handle it (that is, the
+// program has not called os/signal.Notify for the signal).
+func raisebadsignal(sig int32) {
+       if sig == _SIGPROF {
+               // Ignore profiling signals that arrive on non-Go threads.
+               return
+       }
+
+       var handler uintptr
+       if sig >= _NSIG {
+               handler = _SIG_DFL
+       } else {
+               handler = fwdSig[sig]
+       }
+
+       // Reset the signal handler and raise the signal.
+       // We are currently running inside a signal handler, so the
+       // signal is blocked.  We need to unblock it before raising the
+       // signal, or the signal we raise will be ignored until we return
+       // from the signal handler.  We know that the signal was unblocked
+       // before entering the handler, or else we would not have received
+       // it.  That means that we don't have to worry about blocking it
+       // again.
+       unblocksig(sig)
+       setsig(sig, handler, false)
+       raise(sig)
+
+       // If the signal didn't cause the program to exit, restore the
+       // Go signal handler and carry on.
+       //
+       // We may receive another instance of the signal before we
+       // restore the Go handler, but that is not so bad: we know
+       // that the Go program has been ignoring the signal.
+       setsig(sig, funcPC(sighandler), true)
+}
+
 func crash() {
        if GOOS == "darwin" {
                // OS X core dumps are linear dumps of the mapped memory,
index 5e17f747bd13f6f794b21990f95c5dd93fba89ac..d80cc977550033347f0cd5da1f9d155d7225a615 100644 (file)
@@ -203,6 +203,12 @@ func sigdisable(sig uint32) {
 func sigignore(sig uint32) {
 }
 
+func badsignal2()
+
+func raisebadsignal(sig int32) {
+       badsignal2()
+}
+
 func crash() {
        // TODO: This routine should do whatever is needed
        // to make the Windows program abort/crash as it
index 9cfe2592db4d5ba6b22a13765831eb4e00fd4c0a..e078bfaf0e7737323e8ee98643415626eb85e9ca 100644 (file)
@@ -165,5 +165,13 @@ func signal_ignore(s uint32) {
 // This runs on a foreign stack, without an m or a g.  No stack split.
 //go:nosplit
 func badsignal(sig uintptr) {
-       cgocallback(unsafe.Pointer(funcPC(sigsend)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig))
+       cgocallback(unsafe.Pointer(funcPC(badsignalgo)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig))
+}
+
+func badsignalgo(sig uintptr) {
+       if !sigsend(uint32(sig)) {
+               // A foreign thread received the signal sig, and the
+               // Go code does not want to handle it.
+               raisebadsignal(int32(sig))
+       }
 }
index efda4326a4654961cefa1cc12b416dc61d13e3df..3dae2a79df66ed67f0de5f39c31924b3ad02dac4 100644 (file)
@@ -307,9 +307,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
        RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-       MOVL    $3, DI                  // arg 1 - how (SIG_SETMASK)
-       MOVQ    new+0(FP), SI           // arg 2 - set
-       MOVQ    old+8(FP), DX           // arg 3 - oset
+       MOVL    how+0(FP), DI           // arg 1 - how
+       MOVQ    new+8(FP), SI           // arg 2 - set
+       MOVQ    old+16(FP), DX          // arg 3 - oset
        MOVL    $340, AX                // sys_sigprocmask
        SYSCALL
        JAE     2(PC)
index 94b8d95044edc09a2002bdc29cd3a00d50e1b002..be20808a0ec289c1c524961af23373b3768171ef 100644 (file)
@@ -355,10 +355,11 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$16
        MOVL    $0, 0(SP)               // syscall gap
-       MOVL    $3, 4(SP)               // arg 1 - how (SIG_SETMASK)
-       MOVL    new+0(FP), AX
+       MOVL    how+0(FP), AX           // arg 1 - how
+       MOVL    AX, 4(SP)
+       MOVL    new+4(FP), AX
        MOVL    AX, 8(SP)               // arg 2 - set
-       MOVL    old+4(FP), AX
+       MOVL    old+8(FP), AX
        MOVL    AX, 12(SP)              // arg 3 - oset
        MOVL    $340, AX                // sys_sigprocmask
        INT     $0x80
index a9a621b095ef369e407225f514eed7c02ffd7441..8ef04588c3ce6614be25176430e1f0a47d0892bf 100644 (file)
@@ -295,9 +295,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
        RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-       MOVL    $3, DI                  // arg 1 - how (SIG_SETMASK)
-       MOVQ    new+0(FP), SI           // arg 2 - set
-       MOVQ    old+8(FP), DX           // arg 3 - oset
+       MOVL    how+0(FP), DI           // arg 1 - how
+       MOVQ    new+8(FP), SI           // arg 2 - set
+       MOVQ    old+16(FP), DX          // arg 3 - oset
        MOVL    $340, AX                // sys_sigprocmask
        SYSCALL
        JAE     2(PC)
index 3dd04cf97325f0d4b635763c4bc87a13686012ce..298900c9a2b51f211b0a8e28044e86dca064b988 100644 (file)
@@ -327,9 +327,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
        RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-       MOVW $3, R0     // arg 1 - how (SIG_SETMASK)
-       MOVW new+0(FP), R1      // arg 2 - set
-       MOVW old+4(FP), R2      // arg 3 - oset
+       MOVW how+0(FP), R0      // arg 1 - how
+       MOVW new+4(FP), R1      // arg 2 - set
+       MOVW old+8(FP), R2      // arg 3 - oset
        MOVW $SYS_sigprocmask, R7
        SWI $0
        MOVW.CS $0, R8 // crash on syscall failure
index 8644a0b5fd2d43da1baa1b851fd271808b4aba26..59c21c5b42ffa903586a0e979f2fdb899afe7025 100644 (file)
@@ -219,7 +219,7 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
        RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-       MOVQ    sig+8(FP), DI
+       MOVL    sig+8(FP), DI
        MOVQ    info+16(FP), SI
        MOVQ    ctx+24(FP), DX
        MOVQ    fn+0(FP), AX