(root)/
gcc-13.2.0/
libgo/
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  }