github.com/euank/go@v0.0.0-20160829210321-495514729181/misc/cgo/testcshared/main4.c (about) 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 misc/cgo/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 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. 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. 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. 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 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 81 verbose = argc > 2; 82 setvbuf(stdout, NULL, _IONBF, 0); 83 84 // Call setsid so that we can use kill(0, SIGIO) below. 85 // Don't check the return value so that this works both from 86 // a job control shell and from a shell script. 87 setsid(); 88 89 if (verbose) { 90 printf("calling sigaction\n"); 91 } 92 93 memset(&sa, 0, sizeof sa); 94 sa.sa_sigaction = ioHandler; 95 if (sigemptyset(&sa.sa_mask) < 0) { 96 die("sigemptyset"); 97 } 98 sa.sa_flags = SA_SIGINFO; 99 if (sigaction(SIGIO, &sa, NULL) < 0) { 100 die("sigaction"); 101 } 102 103 sa.sa_sigaction = segvHandler; 104 if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) { 105 die("sigaction"); 106 } 107 108 if (verbose) { 109 printf("calling dlopen\n"); 110 } 111 112 handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL); 113 if (handle == NULL) { 114 fprintf(stderr, "%s\n", dlerror()); 115 exit(EXIT_FAILURE); 116 } 117 118 if (verbose) { 119 printf("calling dlsym\n"); 120 } 121 122 // Start some goroutines. 123 fn = (void(*)(void))dlsym(handle, "RunGoroutines"); 124 if (fn == NULL) { 125 fprintf(stderr, "%s\n", dlerror()); 126 exit(EXIT_FAILURE); 127 } 128 129 if (verbose) { 130 printf("calling RunGoroutines\n"); 131 } 132 133 fn(); 134 135 // Block SIGIO in this thread to make it more likely that it 136 // will be delivered to a goroutine. 137 138 if (verbose) { 139 printf("calling pthread_sigmask\n"); 140 } 141 142 if (sigemptyset(&mask) < 0) { 143 die("sigemptyset"); 144 } 145 if (sigaddset(&mask, SIGIO) < 0) { 146 die("sigaddset"); 147 } 148 i = pthread_sigmask(SIG_BLOCK, &mask, NULL); 149 if (i != 0) { 150 fprintf(stderr, "pthread_sigmask: %s\n", strerror(i)); 151 exit(EXIT_FAILURE); 152 } 153 154 if (verbose) { 155 printf("calling kill\n"); 156 } 157 158 if (kill(0, SIGIO) < 0) { 159 die("kill"); 160 } 161 162 if (verbose) { 163 printf("waiting for sigioSeen\n"); 164 } 165 166 // Wait until the signal has been delivered. 167 i = 0; 168 while (!sigioSeen) { 169 if (sched_yield() < 0) { 170 perror("sched_yield"); 171 } 172 i++; 173 if (i > 100000) { 174 fprintf(stderr, "looping too long waiting for signal\n"); 175 exit(EXIT_FAILURE); 176 } 177 } 178 179 if (verbose) { 180 printf("calling setjmp\n"); 181 } 182 183 // Test that a SIGSEGV on this thread is delivered to us. 184 if (setjmp(jmp) == 0) { 185 if (verbose) { 186 printf("triggering SIGSEGV\n"); 187 } 188 189 *nullPointer = '\0'; 190 191 fprintf(stderr, "continued after address error\n"); 192 exit(EXIT_FAILURE); 193 } 194 195 if (verbose) { 196 printf("calling dlsym\n"); 197 } 198 199 // Make sure that a SIGSEGV in Go causes a run-time panic. 200 fn = (void (*)(void))dlsym(handle, "TestSEGV"); 201 if (fn == NULL) { 202 fprintf(stderr, "%s\n", dlerror()); 203 exit(EXIT_FAILURE); 204 } 205 206 if (verbose) { 207 printf("calling TestSEGV\n"); 208 } 209 210 fn(); 211 212 printf("PASS\n"); 213 return 0; 214 }