Text file misc/cgo/testcshared/testdata/main4.c

     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  	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  

View as plain text