1 // Copyright 2015 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 // Test installing a signal handler before the Go code starts.
6 // This is a lot like misc/cgo/testcshared/main4.c.
7 
8 #include <setjmp.h>
9 #include <signal.h>
10 #include <stdarg.h>
11 #include <stddef.h>
12 #include <stdio.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18 #include <sched.h>
19 #include <time.h>
20 #include <errno.h>
21 
22 #include "libgo2.h"
23 
die(const char * msg)24 static void die(const char* msg) {
25 	perror(msg);
26 	exit(EXIT_FAILURE);
27 }
28 
29 static volatile sig_atomic_t sigioSeen;
30 static volatile sig_atomic_t sigpipeSeen;
31 
32 // Use up some stack space.
recur(int i,char * p)33 static void recur(int i, char *p) {
34 	char a[1024];
35 
36 	*p = '\0';
37 	if (i > 0) {
38 		recur(i - 1, a);
39 	}
40 }
41 
pipeHandler(int signo,siginfo_t * info,void * ctxt)42 static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
43 	sigpipeSeen = 1;
44 }
45 
46 // Signal handler that uses up more stack space than a goroutine will have.
ioHandler(int signo,siginfo_t * info,void * ctxt)47 static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
48 	char a[1024];
49 
50 	recur(4, a);
51 	sigioSeen = 1;
52 }
53 
54 static jmp_buf jmp;
55 static char* nullPointer;
56 
57 // An arbitrary function which requires proper stack alignment; see
58 // http://golang.org/issue/17641.
callWithVarargs(void * dummy,...)59 static void callWithVarargs(void* dummy, ...) {
60 	va_list args;
61 	va_start(args, dummy);
62 	va_end(args);
63 }
64 
65 // Signal handler for SIGSEGV on a C thread.
segvHandler(int signo,siginfo_t * info,void * ctxt)66 static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
67 	sigset_t mask;
68 	int i;
69 
70 	// Call an arbitrary function that requires the stack to be properly aligned.
71 	callWithVarargs("dummy arg", 3.1415);
72 
73 	if (sigemptyset(&mask) < 0) {
74 		die("sigemptyset");
75 	}
76 	if (sigaddset(&mask, SIGSEGV) < 0) {
77 		die("sigaddset");
78 	}
79 	i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
80 	if (i != 0) {
81 		fprintf(stderr, "sigprocmask: %s\n", strerror(i));
82 		exit(EXIT_FAILURE);
83 	}
84 
85 	// Don't try this at home.
86 	longjmp(jmp, signo);
87 
88 	// We should never get here.
89 	abort();
90 }
91 
92 // Set up the signal handlers in a high priority constructor,
93 // so that they are installed before the Go code starts.
94 
95 static void init(void) __attribute__ ((constructor (200)));
96 
init()97 static void init() {
98 	struct sigaction sa;
99 
100 	memset(&sa, 0, sizeof sa);
101 	sa.sa_sigaction = ioHandler;
102 	if (sigemptyset(&sa.sa_mask) < 0) {
103 		die("sigemptyset");
104 	}
105 	sa.sa_flags = SA_SIGINFO;
106 	if (sigaction(SIGIO, &sa, NULL) < 0) {
107 		die("sigaction");
108 	}
109 
110 	sa.sa_sigaction = segvHandler;
111 	if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
112 		die("sigaction");
113 	}
114 
115 	sa.sa_sigaction = pipeHandler;
116 	if (sigaction(SIGPIPE, &sa, NULL) < 0) {
117 		die("sigaction");
118 	}
119 }
120 
main(int argc,char ** argv)121 int main(int argc, char** argv) {
122 	int verbose;
123 	sigset_t mask;
124 	int i;
125 	struct timespec ts;
126 	int darwin;
127 
128 	darwin = atoi(argv[1]);
129 
130 	verbose = argc > 2;
131 
132 	setvbuf(stdout, NULL, _IONBF, 0);
133 
134 	// Call setsid so that we can use kill(0, SIGIO) below.
135 	// Don't check the return value so that this works both from
136 	// a job control shell and from a shell script.
137 	setsid();
138 
139 	if (verbose) {
140 		printf("calling RunGoroutines\n");
141 	}
142 
143 	RunGoroutines();
144 
145 	// Block SIGIO in this thread to make it more likely that it
146 	// will be delivered to a goroutine.
147 
148 	if (verbose) {
149 		printf("calling pthread_sigmask\n");
150 	}
151 
152 	if (sigemptyset(&mask) < 0) {
153 		die("sigemptyset");
154 	}
155 	if (sigaddset(&mask, SIGIO) < 0) {
156 		die("sigaddset");
157 	}
158 	i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
159 	if (i != 0) {
160 		fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
161 		exit(EXIT_FAILURE);
162 	}
163 
164 	if (verbose) {
165 		printf("calling kill\n");
166 	}
167 
168 	if (kill(0, SIGIO) < 0) {
169 		die("kill");
170 	}
171 
172 	if (verbose) {
173 		printf("waiting for sigioSeen\n");
174 	}
175 
176 	// Wait until the signal has been delivered.
177 	i = 0;
178 	while (!sigioSeen) {
179 		ts.tv_sec = 0;
180 		ts.tv_nsec = 1000000;
181 		nanosleep(&ts, NULL);
182 		i++;
183 		if (i > 5000) {
184 			fprintf(stderr, "looping too long waiting for SIGIO\n");
185 			exit(EXIT_FAILURE);
186 		}
187 	}
188 
189 	if (verbose) {
190 		printf("provoking SIGPIPE\n");
191 	}
192 
193 	// SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384.
194 	if (!darwin) {
195 		GoRaiseSIGPIPE();
196 
197 		if (verbose) {
198 			printf("waiting for sigpipeSeen\n");
199 		}
200 
201 		// Wait until the signal has been delivered.
202 		i = 0;
203 		while (!sigpipeSeen) {
204 			ts.tv_sec = 0;
205 			ts.tv_nsec = 1000000;
206 			nanosleep(&ts, NULL);
207 			i++;
208 			if (i > 5000) {
209 				fprintf(stderr, "looping too long waiting for SIGPIPE\n");
210 				exit(EXIT_FAILURE);
211 			}
212 		}
213 	}
214 
215 	if (verbose) {
216 		printf("calling setjmp\n");
217 	}
218 
219 	// Test that a SIGSEGV on this thread is delivered to us.
220 	if (setjmp(jmp) == 0) {
221 		if (verbose) {
222 			printf("triggering SIGSEGV\n");
223 		}
224 
225 		*nullPointer = '\0';
226 
227 		fprintf(stderr, "continued after address error\n");
228 		exit(EXIT_FAILURE);
229 	}
230 
231 	if (verbose) {
232 		printf("calling TestSEGV\n");
233 	}
234 
235 	TestSEGV();
236 
237 	printf("PASS\n");
238 	return 0;
239 }
240