Skip to content

Commit 872b168

Browse files
runtime: if we don't handle a signal on a non-Go thread, raise it
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 <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]>
1 parent 428ed1e commit 872b168

26 files changed

+215
-39
lines changed

src/runtime/crash_cgo_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
8282
}
8383
}
8484

85+
func TestCgoExternalThreadSignal(t *testing.T) {
86+
// issue 10139
87+
switch runtime.GOOS {
88+
case "plan9", "windows":
89+
t.Skipf("no pthreads on %s", runtime.GOOS)
90+
}
91+
got := executeTest(t, cgoExternalThreadSignalSource, nil)
92+
want := "OK\n"
93+
if got != want {
94+
t.Fatalf("expected %q, but got %q", want, got)
95+
}
96+
}
97+
8598
func TestCgoDLLImports(t *testing.T) {
8699
// test issue 9356
87100
if runtime.GOOS != "windows" {
@@ -282,6 +295,60 @@ func main() {
282295
}
283296
`
284297

298+
const cgoExternalThreadSignalSource = `
299+
package main
300+
301+
/*
302+
#include <pthread.h>
303+
304+
void **nullptr;
305+
306+
void *crash(void *p) {
307+
*nullptr = p;
308+
return 0;
309+
}
310+
311+
int start_crashing_thread(void) {
312+
pthread_t tid;
313+
return pthread_create(&tid, 0, crash, 0);
314+
}
315+
*/
316+
import "C"
317+
318+
import (
319+
"fmt"
320+
"os"
321+
"os/exec"
322+
"time"
323+
)
324+
325+
func main() {
326+
if len(os.Args) > 1 && os.Args[1] == "crash" {
327+
i := C.start_crashing_thread()
328+
if i != 0 {
329+
fmt.Println("pthread_create failed:", i)
330+
// Exit with 0 because parent expects us to crash.
331+
return
332+
}
333+
334+
// We should crash immediately, but give it plenty of
335+
// time before failing (by exiting 0) in case we are
336+
// running on a slow system.
337+
time.Sleep(5 * time.Second)
338+
return
339+
}
340+
341+
out, err := exec.Command(os.Args[0], "crash").CombinedOutput()
342+
if err == nil {
343+
fmt.Println("C signal did not crash as expected\n")
344+
fmt.Printf("%s\n", out)
345+
os.Exit(1)
346+
}
347+
348+
fmt.Println("OK")
349+
}
350+
`
351+
285352
const cgoDLLImportsMainSource = `
286353
package main
287354

src/runtime/os1_darwin.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,8 @@ func signalstack(s *stack) {
469469
func updatesigmask(m sigmask) {
470470
sigprocmask(_SIG_SETMASK, &m[0], nil)
471471
}
472+
473+
func unblocksig(sig int32) {
474+
mask := uint32(1) << (uint32(sig) - 1)
475+
sigprocmask(_SIG_UNBLOCK, &mask, nil)
476+
}

src/runtime/os1_dragonfly.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
7878
}
7979

8080
var oset sigset
81-
sigprocmask(&sigset_all, &oset)
81+
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
8282

8383
params := lwpparams{
8484
start_func: funcPC(lwp_start),
@@ -91,7 +91,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
9191
mp.tls[0] = uintptr(mp.id) // XXX so 386 asm can find it
9292

9393
lwp_create(&params)
94-
sigprocmask(&oset, nil)
94+
sigprocmask(_SIG_SETMASK, &oset, nil)
9595
}
9696

9797
func osinit() {
@@ -124,7 +124,7 @@ func msigsave(mp *m) {
124124
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
125125
throw("insufficient storage for signal mask")
126126
}
127-
sigprocmask(nil, smask)
127+
sigprocmask(_SIG_SETMASK, nil, smask)
128128
}
129129

130130
// Called to initialize a new m (including the bootstrap m).
@@ -145,14 +145,14 @@ func minit() {
145145
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
146146
}
147147
}
148-
sigprocmask(&nmask, nil)
148+
sigprocmask(_SIG_SETMASK, &nmask, nil)
149149
}
150150

151151
// Called from dropm to undo the effect of an minit.
152152
func unminit() {
153153
_g_ := getg()
154154
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
155-
sigprocmask(smask, nil)
155+
sigprocmask(_SIG_SETMASK, smask, nil)
156156
signalstack(nil)
157157
}
158158

@@ -237,5 +237,11 @@ func signalstack(s *stack) {
237237
func updatesigmask(m sigmask) {
238238
var mask sigset
239239
copy(mask.__bits[:], m[:])
240-
sigprocmask(&mask, nil)
240+
sigprocmask(_SIG_SETMASK, &mask, nil)
241+
}
242+
243+
func unblocksig(sig int32) {
244+
var mask sigset
245+
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
246+
sigprocmask(_SIG_UNBLOCK, &mask, nil)
241247
}

src/runtime/os1_freebsd.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
8888
mp.tls[0] = uintptr(mp.id) // so 386 asm can find it
8989

9090
var oset sigset
91-
sigprocmask(&sigset_all, &oset)
91+
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
9292
thr_new(&param, int32(unsafe.Sizeof(param)))
93-
sigprocmask(&oset, nil)
93+
sigprocmask(_SIG_SETMASK, &oset, nil)
9494
}
9595

9696
func osinit() {
@@ -123,7 +123,7 @@ func msigsave(mp *m) {
123123
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
124124
throw("insufficient storage for signal mask")
125125
}
126-
sigprocmask(nil, smask)
126+
sigprocmask(_SIG_SETMASK, nil, smask)
127127
}
128128

129129
// Called to initialize a new m (including the bootstrap m).
@@ -147,14 +147,14 @@ func minit() {
147147
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
148148
}
149149
}
150-
sigprocmask(&nmask, nil)
150+
sigprocmask(_SIG_SETMASK, &nmask, nil)
151151
}
152152

153153
// Called from dropm to undo the effect of an minit.
154154
func unminit() {
155155
_g_ := getg()
156156
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
157-
sigprocmask(smask, nil)
157+
sigprocmask(_SIG_SETMASK, smask, nil)
158158
signalstack(nil)
159159
}
160160

@@ -239,5 +239,11 @@ func signalstack(s *stack) {
239239
func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
240240
var mask sigset
241241
copy(mask.__bits[:], m[:])
242-
sigprocmask(&mask, nil)
242+
sigprocmask(_SIG_SETMASK, &mask, nil)
243+
}
244+
245+
func unblocksig(sig int32) {
246+
var mask sigset
247+
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
248+
sigprocmask(_SIG_UNBLOCK, &mask, nil)
243249
}

src/runtime/os1_linux.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,9 @@ func updatesigmask(m sigmask) {
333333
copy(mask[:], m[:])
334334
rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
335335
}
336+
337+
func unblocksig(sig int32) {
338+
var mask sigset
339+
mask[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
340+
rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
341+
}

src/runtime/os1_nacl.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ func badsignal2() {
170170

171171
var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n")
172172

173+
func raisebadsignal(sig int32) {
174+
badsignal2()
175+
}
176+
173177
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {}
174178
func munmap(addr unsafe.Pointer, n uintptr) {}
175179
func resetcpuprofiler(hz int32) {}

src/runtime/os1_netbsd.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,9 @@ func updatesigmask(m sigmask) {
230230
copy(mask.__bits[:], m[:])
231231
sigprocmask(_SIG_SETMASK, &mask, nil)
232232
}
233+
234+
func unblocksig(sig int32) {
235+
var mask sigset
236+
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
237+
sigprocmask(_SIG_UNBLOCK, &mask, nil)
238+
}

src/runtime/os1_openbsd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,8 @@ func signalstack(s *stack) {
239239
func updatesigmask(m sigmask) {
240240
sigprocmask(_SIG_SETMASK, m[0])
241241
}
242+
243+
func unblocksig(sig int32) {
244+
mask := uint32(1) << (uint32(sig) - 1)
245+
sigprocmask(_SIG_UNBLOCK, mask)
246+
}

src/runtime/os1_plan9.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ func badsignal2() {
254254
exits(&_badsignal[0])
255255
}
256256

257+
func raisebadsignal(sig int32) {
258+
badsignal2()
259+
}
260+
257261
func _atoi(b []byte) int {
258262
n := 0
259263
for len(b) > 0 && '0' <= b[0] && b[0] <= '9' {

src/runtime/os2_dragonfly.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
package runtime
66

77
const (
8-
_NSIG = 33
9-
_SI_USER = 0x10001
10-
_SS_DISABLE = 4
11-
_RLIMIT_AS = 10
8+
_NSIG = 33
9+
_SI_USER = 0x10001
10+
_SS_DISABLE = 4
11+
_RLIMIT_AS = 10
12+
_SIG_BLOCK = 1
13+
_SIG_UNBLOCK = 2
14+
_SIG_SETMASK = 3
1215
)

src/runtime/os2_freebsd.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
package runtime
66

77
const (
8-
_SS_DISABLE = 4
9-
_NSIG = 33
10-
_SI_USER = 0x10001
11-
_RLIMIT_AS = 10
8+
_SS_DISABLE = 4
9+
_NSIG = 33
10+
_SI_USER = 0x10001
11+
_RLIMIT_AS = 10
12+
_SIG_BLOCK = 1
13+
_SIG_UNBLOCK = 2
14+
_SIG_SETMASK = 3
1215
)

src/runtime/os2_linux.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const (
88
_SS_DISABLE = 2
99
_NSIG = 65
1010
_SI_USER = 0
11+
_SIG_BLOCK = 0
12+
_SIG_UNBLOCK = 1
1113
_SIG_SETMASK = 2
1214
_RLIMIT_AS = 9
1315
)

src/runtime/os2_solaris.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package runtime
66

77
const (
88
_SS_DISABLE = 2
9+
_SIG_UNBLOCK = 2
910
_SIG_SETMASK = 3
1011
_NSIG = 73 /* number of signals in sigtable array */
1112
_SI_USER = 0

src/runtime/os3_solaris.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,12 @@ func updatesigmask(m sigmask) {
304304
sigprocmask(_SIG_SETMASK, &mask, nil)
305305
}
306306

307+
func unblocksig(sig int32) {
308+
var mask sigset
309+
mask.__sigbits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
310+
sigprocmask(_SIG_UNBLOCK, &mask, nil)
311+
}
312+
307313
//go:nosplit
308314
func semacreate() uintptr {
309315
var sem *semt

src/runtime/os_darwin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func mach_thread_self() uint32
2020
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
2121

2222
//go:noescape
23-
func sigprocmask(sig uint32, new, old *uint32)
23+
func sigprocmask(how uint32, new, old *uint32)
2424

2525
//go:noescape
2626
func sigaction(mode uint32, new, old *sigactiont)

src/runtime/os_dragonfly.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
1919
func sigaction(sig int32, new, old *sigactiont)
2020

2121
//go:noescape
22-
func sigprocmask(new, old *sigset)
22+
func sigprocmask(how int32, new, old *sigset)
2323

2424
//go:noescape
2525
func setitimer(mode int32, new, old *itimerval)

src/runtime/os_freebsd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
1919
func sigaction(sig int32, new, old *sigactiont)
2020

2121
//go:noescape
22-
func sigprocmask(new, old *sigset)
22+
func sigprocmask(how int32, new, old *sigset)
2323

2424
//go:noescape
2525
func setitimer(mode int32, new, old *itimerval)

src/runtime/os_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ func rtsigprocmask(sig uint32, new, old *sigset, size int32)
2929

3030
//go:noescape
3131
func getrlimit(kind int32, limit unsafe.Pointer) int32
32-
func raise(sig uint32)
33-
func raiseproc(sig uint32)
32+
func raise(sig int32)
33+
func raiseproc(sig int32)
3434

3535
//go:noescape
3636
func sched_getaffinity(pid, len uintptr, buf *uintptr) int32

src/runtime/signal1_unix.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,43 @@ func sigpipe() {
140140
raise(_SIGPIPE)
141141
}
142142

143+
// raisebadsignal is called when a signal is received on a non-Go
144+
// thread, and the Go program does not want to handle it (that is, the
145+
// program has not called os/signal.Notify for the signal).
146+
func raisebadsignal(sig int32) {
147+
if sig == _SIGPROF {
148+
// Ignore profiling signals that arrive on non-Go threads.
149+
return
150+
}
151+
152+
var handler uintptr
153+
if sig >= _NSIG {
154+
handler = _SIG_DFL
155+
} else {
156+
handler = fwdSig[sig]
157+
}
158+
159+
// Reset the signal handler and raise the signal.
160+
// We are currently running inside a signal handler, so the
161+
// signal is blocked. We need to unblock it before raising the
162+
// signal, or the signal we raise will be ignored until we return
163+
// from the signal handler. We know that the signal was unblocked
164+
// before entering the handler, or else we would not have received
165+
// it. That means that we don't have to worry about blocking it
166+
// again.
167+
unblocksig(sig)
168+
setsig(sig, handler, false)
169+
raise(sig)
170+
171+
// If the signal didn't cause the program to exit, restore the
172+
// Go signal handler and carry on.
173+
//
174+
// We may receive another instance of the signal before we
175+
// restore the Go handler, but that is not so bad: we know
176+
// that the Go program has been ignoring the signal.
177+
setsig(sig, funcPC(sighandler), true)
178+
}
179+
143180
func crash() {
144181
if GOOS == "darwin" {
145182
// OS X core dumps are linear dumps of the mapped memory,

0 commit comments

Comments
 (0)