• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 that a signal handler that uses up stack space does not crash
6 // if the signal is delivered to a thread running a goroutine.
7 // This is a lot like ../testcarchive/main2.c.
8 
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stddef.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <sched.h>
18 #include <time.h>
19 #include <dlfcn.h>
20 
die(const char * msg)21 static void die(const char* msg) {
22 	perror(msg);
23 	exit(EXIT_FAILURE);
24 }
25 
26 static volatile sig_atomic_t sigioSeen;
27 
28 // Use up some stack space.
recur(int i,char * p)29 static void recur(int i, char *p) {
30 	char a[1024];
31 
32 	*p = '\0';
33 	if (i > 0) {
34 		recur(i - 1, a);
35 	}
36 }
37 
38 // Signal handler that uses up more stack space than a goroutine will have.
ioHandler(int signo,siginfo_t * info,void * ctxt)39 static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
40 	char a[1024];
41 
42 	recur(4, a);
43 	sigioSeen = 1;
44 }
45 
46 static jmp_buf jmp;
47 static char* nullPointer;
48 
49 // Signal handler for SIGSEGV on a C thread.
segvHandler(int signo,siginfo_t * info,void * ctxt)50 static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
51 	sigset_t mask;
52 	int i;
53 
54 	if (sigemptyset(&mask) < 0) {
55 		die("sigemptyset");
56 	}
57 	if (sigaddset(&mask, SIGSEGV) < 0) {
58 		die("sigaddset");
59 	}
60 	i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
61 	if (i != 0) {
62 		fprintf(stderr, "sigprocmask: %s\n", strerror(i));
63 		exit(EXIT_FAILURE);
64 	}
65 
66 	// Don't try this at home.
67 	longjmp(jmp, signo);
68 
69 	// We should never get here.
70 	abort();
71 }
72 
main(int argc,char ** argv)73 int main(int argc, char** argv) {
74 	int verbose;
75 	struct sigaction sa;
76 	void* handle;
77 	void (*fn)(void);
78 	sigset_t mask;
79 	int i;
80 	struct timespec ts;
81 
82 	verbose = argc > 2;
83 	setvbuf(stdout, NULL, _IONBF, 0);
84 
85 	// Call setsid so that we can use kill(0, SIGIO) below.
86 	// Don't check the return value so that this works both from
87 	// a job control shell and from a shell script.
88 	setsid();
89 
90 	if (verbose) {
91 		fprintf(stderr, "calling sigaction\n");
92 	}
93 
94 	memset(&sa, 0, sizeof sa);
95 	sa.sa_sigaction = ioHandler;
96 	if (sigemptyset(&sa.sa_mask) < 0) {
97 		die("sigemptyset");
98 	}
99 	sa.sa_flags = SA_SIGINFO;
100 	if (sigaction(SIGIO, &sa, NULL) < 0) {
101 		die("sigaction");
102 	}
103 
104 	sa.sa_sigaction = segvHandler;
105 	if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
106 		die("sigaction");
107 	}
108 
109 	if (verbose) {
110 		fprintf(stderr, "calling dlopen\n");
111 	}
112 
113 	handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
114 	if (handle == NULL) {
115 		fprintf(stderr, "%s\n", dlerror());
116 		exit(EXIT_FAILURE);
117 	}
118 
119 	if (verbose) {
120 		fprintf(stderr, "calling dlsym\n");
121 	}
122 
123 	// Start some goroutines.
124 	fn = (void(*)(void))dlsym(handle, "RunGoroutines");
125 	if (fn == NULL) {
126 		fprintf(stderr, "%s\n", dlerror());
127 		exit(EXIT_FAILURE);
128 	}
129 
130 	if (verbose) {
131 		fprintf(stderr, "calling RunGoroutines\n");
132 	}
133 
134 	fn();
135 
136 	// Block SIGIO in this thread to make it more likely that it
137 	// will be delivered to a goroutine.
138 
139 	if (verbose) {
140 		fprintf(stderr, "calling pthread_sigmask\n");
141 	}
142 
143 	if (sigemptyset(&mask) < 0) {
144 		die("sigemptyset");
145 	}
146 	if (sigaddset(&mask, SIGIO) < 0) {
147 		die("sigaddset");
148 	}
149 	i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
150 	if (i != 0) {
151 		fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
152 		exit(EXIT_FAILURE);
153 	}
154 
155 	if (verbose) {
156 		fprintf(stderr, "calling kill\n");
157 	}
158 
159 	if (kill(0, SIGIO) < 0) {
160 		die("kill");
161 	}
162 
163 	if (verbose) {
164 		fprintf(stderr, "waiting for sigioSeen\n");
165 	}
166 
167 	// Wait until the signal has been delivered.
168 	i = 0;
169 	while (!sigioSeen) {
170 		ts.tv_sec = 0;
171 		ts.tv_nsec = 1000000;
172 		nanosleep(&ts, NULL);
173 		i++;
174 		if (i > 5000) {
175 			fprintf(stderr, "looping too long waiting for signal\n");
176 			exit(EXIT_FAILURE);
177 		}
178 	}
179 
180 	if (verbose) {
181 		fprintf(stderr, "calling setjmp\n");
182 	}
183 
184 	// Test that a SIGSEGV on this thread is delivered to us.
185 	if (setjmp(jmp) == 0) {
186 		if (verbose) {
187 			fprintf(stderr, "triggering SIGSEGV\n");
188 		}
189 
190 		*nullPointer = '\0';
191 
192 		fprintf(stderr, "continued after address error\n");
193 		exit(EXIT_FAILURE);
194 	}
195 
196 	if (verbose) {
197 		fprintf(stderr, "calling dlsym\n");
198 	}
199 
200 	// Make sure that a SIGSEGV in Go causes a run-time panic.
201 	fn = (void (*)(void))dlsym(handle, "TestSEGV");
202 	if (fn == NULL) {
203 		fprintf(stderr, "%s\n", dlerror());
204 		exit(EXIT_FAILURE);
205 	}
206 
207 	if (verbose) {
208 		fprintf(stderr, "calling TestSEGV\n");
209 	}
210 
211 	fn();
212 
213 	printf("PASS\n");
214 	return 0;
215 }
216