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 21static void die(const char* msg) { 22 perror(msg); 23 exit(EXIT_FAILURE); 24} 25 26static volatile sig_atomic_t sigioSeen; 27 28// Use up some stack space. 29static 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. 39static void ioHandler(int signo, siginfo_t* info, void* ctxt) { 40 char a[1024]; 41 42 recur(4, a); 43 sigioSeen = 1; 44} 45 46static jmp_buf jmp; 47static char* nullPointer; 48 49// Signal handler for SIGSEGV on a C thread. 50static 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 73int 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 printf("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 printf("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 printf("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 printf("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 printf("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 printf("calling kill\n"); 157 } 158 159 if (kill(0, SIGIO) < 0) { 160 die("kill"); 161 } 162 163 if (verbose) { 164 printf("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 printf("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 printf("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 printf("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 printf("calling TestSEGV\n"); 209 } 210 211 fn(); 212 213 printf("PASS\n"); 214 return 0; 215} 216