Skip to content

Commit 2f1ead7

Browse files
committed
runtime: correctly handle signals received on foreign threads
Fixes #3250. R=rsc CC=golang-dev https://golang.org/cl/10757044
1 parent 2a983aa commit 2f1ead7

25 files changed

+172
-159
lines changed

misc/cgo/test/cgo_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ func TestCflags(t *testing.T) { testCflags(t) }
4242
func Test5337(t *testing.T) { test5337(t) }
4343
func Test5548(t *testing.T) { test5548(t) }
4444
func Test5603(t *testing.T) { test5603(t) }
45+
func Test3250(t *testing.T) { test3250(t) }
4546

4647
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }

misc/cgo/test/issue3250.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2013 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build !windows
6+
7+
package cgotest
8+
9+
/*
10+
#include <signal.h>
11+
#include <pthread.h>
12+
#include <unistd.h>
13+
#include <stdlib.h>
14+
15+
static void *thread(void *p) {
16+
(void)p;
17+
const int M = 100;
18+
int i;
19+
for (i = 0; i < M; i++) {
20+
pthread_kill(pthread_self(), SIGCHLD);
21+
usleep(rand() % 20 + 5);
22+
}
23+
return NULL;
24+
}
25+
void testSendSIG() {
26+
const int N = 20;
27+
int i;
28+
pthread_t tid[N];
29+
for (i = 0; i < N; i++) {
30+
usleep(rand() % 200 + 100);
31+
pthread_create(&tid[i], 0, thread, NULL);
32+
}
33+
for (i = 0; i < N; i++)
34+
pthread_join(tid[i], 0);
35+
}
36+
*/
37+
import "C"
38+
39+
import (
40+
"os"
41+
"os/signal"
42+
"syscall"
43+
"testing"
44+
"time"
45+
)
46+
47+
func test3250(t *testing.T) {
48+
const (
49+
thres = 5
50+
sig = syscall.SIGCHLD
51+
)
52+
type result struct {
53+
n int
54+
sig os.Signal
55+
}
56+
var (
57+
sigCh = make(chan os.Signal, 10)
58+
waitStart = make(chan struct{})
59+
waitDone = make(chan result)
60+
)
61+
62+
signal.Notify(sigCh, sig)
63+
64+
go func() {
65+
n := 0
66+
alarm := time.After(time.Second * 3)
67+
for {
68+
select {
69+
case <-waitStart:
70+
waitStart = nil
71+
case v := <-sigCh:
72+
n++
73+
if v != sig || n > thres {
74+
waitDone <- result{n, v}
75+
return
76+
}
77+
case <-alarm:
78+
waitDone <- result{n, sig}
79+
return
80+
}
81+
}
82+
}()
83+
84+
waitStart <- struct{}{}
85+
C.testSendSIG()
86+
r := <-waitDone
87+
if r.sig != sig {
88+
t.Fatalf("received signal %v, but want %v", r.sig, sig)
89+
}
90+
t.Logf("got %d signals\n", r.n)
91+
if r.n <= thres {
92+
t.Fatalf("expected more than %d", thres)
93+
}
94+
}

misc/cgo/test/issue3250w.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2013 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build windows
6+
7+
package cgotest
8+
9+
import "testing"
10+
11+
func test3250(t *testing.T) {}

src/pkg/runtime/os_darwin.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -523,30 +523,6 @@ runtime·setprof(bool on)
523523
runtime·sigprocmask(SIG_BLOCK, &sigset_prof, nil);
524524
}
525525

526-
#pragma dataflag 16 // no pointers
527-
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
528-
529-
// This runs on a foreign stack, without an m or a g. No stack split.
530-
#pragma textflag 7
531-
void
532-
runtime·badsignal(int32 sig)
533-
{
534-
int32 len;
535-
536-
if (sig == SIGPROF) {
537-
return; // Ignore SIGPROFs intended for a non-Go thread.
538-
}
539-
runtime·write(2, badsignal, sizeof badsignal - 1);
540-
if (0 <= sig && sig < NSIG) {
541-
// Can't call findnull() because it will split stack.
542-
for(len = 0; runtime·sigtab[sig].name[len]; len++)
543-
;
544-
runtime·write(2, runtime·sigtab[sig].name, len);
545-
}
546-
runtime·write(2, "\n", 1);
547-
runtime·exit(1);
548-
}
549-
550526
void
551527
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
552528
{

src/pkg/runtime/os_freebsd.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -235,30 +235,6 @@ runtime·setprof(bool on)
235235
USED(on);
236236
}
237237

238-
#pragma dataflag 16 // no pointers
239-
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
240-
241-
// This runs on a foreign stack, without an m or a g. No stack split.
242-
#pragma textflag 7
243-
void
244-
runtime·badsignal(int32 sig)
245-
{
246-
int32 len;
247-
248-
if (sig == SIGPROF) {
249-
return; // Ignore SIGPROFs intended for a non-Go thread.
250-
}
251-
runtime·write(2, badsignal, sizeof badsignal - 1);
252-
if (0 <= sig && sig < NSIG) {
253-
// Can't call findnull() because it will split stack.
254-
for(len = 0; runtime·sigtab[sig].name[len]; len++)
255-
;
256-
runtime·write(2, runtime·sigtab[sig].name, len);
257-
}
258-
runtime·write(2, "\n", 1);
259-
runtime·exit(1);
260-
}
261-
262238
extern void runtime·sigtramp(void);
263239

264240
typedef struct sigaction {

src/pkg/runtime/os_linux.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -284,30 +284,6 @@ runtime·setprof(bool on)
284284
USED(on);
285285
}
286286

287-
#pragma dataflag 16 // no pointers
288-
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
289-
290-
// This runs on a foreign stack, without an m or a g. No stack split.
291-
#pragma textflag 7
292-
void
293-
runtime·badsignal(int32 sig)
294-
{
295-
int32 len;
296-
297-
if (sig == SIGPROF) {
298-
return; // Ignore SIGPROFs intended for a non-Go thread.
299-
}
300-
runtime·write(2, badsignal, sizeof badsignal - 1);
301-
if (0 <= sig && sig < NSIG) {
302-
// Can't call findnull() because it will split stack.
303-
for(len = 0; runtime·sigtab[sig].name[len]; len++)
304-
;
305-
runtime·write(2, runtime·sigtab[sig].name, len);
306-
}
307-
runtime·write(2, "\n", 1);
308-
runtime·exit(1);
309-
}
310-
311287
#ifdef GOARCH_386
312288
#define sa_handler k_sa_handler
313289
#endif

src/pkg/runtime/os_netbsd.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -275,30 +275,6 @@ runtime·setprof(bool on)
275275
USED(on);
276276
}
277277

278-
#pragma dataflag 16 // no pointers
279-
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
280-
281-
// This runs on a foreign stack, without an m or a g. No stack split.
282-
#pragma textflag 7
283-
void
284-
runtime·badsignal(int32 sig)
285-
{
286-
int32 len;
287-
288-
if (sig == SIGPROF) {
289-
return; // Ignore SIGPROFs intended for a non-Go thread.
290-
}
291-
runtime·write(2, badsignal, sizeof badsignal - 1);
292-
if (0 <= sig && sig < NSIG) {
293-
// Can't call findnull() because it will split stack.
294-
for(len = 0; runtime·sigtab[sig].name[len]; len++)
295-
;
296-
runtime·write(2, runtime·sigtab[sig].name, len);
297-
}
298-
runtime·write(2, "\n", 1);
299-
runtime·exit(1);
300-
}
301-
302278
extern void runtime·sigtramp(void);
303279

304280
typedef struct sigaction {

src/pkg/runtime/os_openbsd.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -257,30 +257,6 @@ runtime·setprof(bool on)
257257
USED(on);
258258
}
259259

260-
#pragma dataflag 16 // no pointers
261-
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
262-
263-
// This runs on a foreign stack, without an m or a g. No stack split.
264-
#pragma textflag 7
265-
void
266-
runtime·badsignal(int32 sig)
267-
{
268-
int32 len;
269-
270-
if (sig == SIGPROF) {
271-
return; // Ignore SIGPROFs intended for a non-Go thread.
272-
}
273-
runtime·write(2, badsignal, sizeof badsignal - 1);
274-
if (0 <= sig && sig < NSIG) {
275-
// Can't call findnull() because it will split stack.
276-
for(len = 0; runtime·sigtab[sig].name[len]; len++)
277-
;
278-
runtime·write(2, runtime·sigtab[sig].name, len);
279-
}
280-
runtime·write(2, "\n", 1);
281-
runtime·exit(1);
282-
}
283-
284260
extern void runtime·sigtramp(void);
285261

286262
typedef struct sigaction {

src/pkg/runtime/os_plan9.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
336336
// This runs on a foreign stack, without an m or a g. No stack split.
337337
#pragma textflag 7
338338
void
339-
runtime·badsignal(void)
339+
runtime·badsignal2(void)
340340
{
341341
runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL);
342342
runtime·exits(badsignal);

src/pkg/runtime/sigqueue.goc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ package runtime
2828
#include "runtime.h"
2929
#include "defs_GOOS_GOARCH.h"
3030
#include "os_GOOS.h"
31+
#include "cgocall.h"
3132

3233
static struct {
3334
Note;
@@ -155,3 +156,11 @@ func signal_disable(s uint32) {
155156
sig.wanted[s/32] &= ~(1U<<(s&31));
156157
runtime·sigdisable(s);
157158
}
159+
160+
// This runs on a foreign stack, without an m or a g. No stack split.
161+
#pragma textflag 7
162+
void
163+
runtime·badsignal(uintptr sig)
164+
{
165+
runtime·cgocallback((void (*)(void))runtime·sigsend, &sig, sizeof(sig));
166+
}

src/pkg/runtime/sys_darwin_386.s

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,12 @@ TEXT runtime·sigtramp(SB),7,$40
238238
// check that m exists
239239
MOVL m(CX), BP
240240
CMPL BP, $0
241-
JNE 5(PC)
241+
JNE 6(PC)
242242
MOVL sig+8(FP), BX
243243
MOVL BX, 0(SP)
244-
CALL runtime·badsignal(SB)
245-
RET
244+
MOVL $runtime·badsignal(SB), AX
245+
CALL AX
246+
JMP sigtramp_ret
246247

247248
// save g
248249
MOVL g(CX), DI
@@ -269,6 +270,7 @@ TEXT runtime·sigtramp(SB),7,$40
269270
MOVL 20(SP), DI
270271
MOVL DI, g(CX)
271272

273+
sigtramp_ret:
272274
// call sigreturn
273275
MOVL context+16(FP), CX
274276
MOVL style+4(FP), BX

src/pkg/runtime/sys_darwin_amd64.s

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,17 @@ TEXT runtime·sigaction(SB),7,$0
192192
TEXT runtime·sigtramp(SB),7,$64
193193
get_tls(BX)
194194

195+
MOVQ R8, 32(SP) // save ucontext
196+
MOVQ SI, 40(SP) // save infostyle
197+
195198
// check that m exists
196199
MOVQ m(BX), BP
197200
CMPQ BP, $0
198-
JNE 4(PC)
201+
JNE 5(PC)
199202
MOVL DX, 0(SP)
200-
CALL runtime·badsignal(SB)
201-
RET
203+
MOVQ $runtime·badsignal(SB), AX
204+
CALL AX
205+
JMP sigtramp_ret
202206

203207
// save g
204208
MOVQ g(BX), R10
@@ -213,15 +217,14 @@ TEXT runtime·sigtramp(SB),7,$64
213217
MOVQ R8, 16(SP)
214218
MOVQ R10, 24(SP)
215219

216-
MOVQ R8, 32(SP) // save ucontext
217-
MOVQ SI, 40(SP) // save infostyle
218220
CALL DI
219221

220222
// restore g
221223
get_tls(BX)
222224
MOVQ 48(SP), R10
223225
MOVQ R10, g(BX)
224226

227+
sigtramp_ret:
225228
// call sigreturn
226229
MOVL $(0x2000000+184), AX // sigreturn(ucontext, infostyle)
227230
MOVQ 32(SP), DI // saved ucontext

src/pkg/runtime/sys_freebsd_386.s

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,12 @@ TEXT runtime·sigtramp(SB),7,$44
183183
// check that m exists
184184
MOVL m(CX), BX
185185
CMPL BX, $0
186-
JNE 5(PC)
186+
JNE 6(PC)
187187
MOVL signo+0(FP), BX
188188
MOVL BX, 0(SP)
189-
CALL runtime·badsignal(SB)
190-
RET
189+
MOVL $runtime·badsignal(SB), AX
190+
CALL AX
191+
JMP sigtramp_ret
191192

192193
// save g
193194
MOVL g(CX), DI
@@ -212,7 +213,8 @@ TEXT runtime·sigtramp(SB),7,$44
212213
get_tls(CX)
213214
MOVL 20(SP), BX
214215
MOVL BX, g(CX)
215-
216+
217+
sigtramp_ret:
216218
// call sigreturn
217219
MOVL context+8(FP), AX
218220
MOVL $0, 0(SP) // syscall gap

0 commit comments

Comments
 (0)