About catching the SIGSEGV in multithreaded environment
c, linux, signals
Solution
Your code does not call sigaction(2), and I believe it should call it. Read also signal(7) and signal-safety(7). And the signal action (thru `sa_sigaction` field should do something (machine specific) with its `siginfo_t` to skip the offending machine instruction, or to `mmap` the offending address, or call `siglongjmp`, otherwise when returning from the signal handler you'll get the `SIGSEGV` again since the offending machine instruction is restarted.
You cannot handle the `SIGSEGV` in another thread, since synchronous signals (such as `SIGSEGV` or `SIGSYS`) are thread specific (see this answer), so what you try to achieve with `sigwaitinfo` cannot work. In particular `SIGSEGV` is directed to the offending thread.
Read also all about Linux signals.
PS. An example of clever `SIGSEGV` handling is offered by the no-more maintained (in May 2019) Ravenbrook MPS garbage collector library. Notice also the Linux specific and recent userfaultfd(2) and signalfd(2) system calls.
Problem
I'd like to know if it is possible/the recommended way to catch the `SIGSEGV` signal in multithreaded environment. I am particularly interested in handling the `SIGSEGV` raised by something like `*((int *)0) = 0`. Some reading on this topic led me to `signal()` and `sigaction()`, which install a signal handler. While neither seem promising in multithreaded environment. I then tried the `sigwaitinfo()`, receiving the signals in one thread with a prior `pthread_sigmask()` call that blocks the signal on the others. It worked to the extent upon which the signal `SIGSEGV` was raised, using raise(), inside a thread or when it was sent to the process by something like `kill -SIGSEGV`; however, `\*((int*)0) = 0` still kills the process. My test program is as follows ``` void block_signal() { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSEGV); sigprocmask(SIG_BLOCK, &set, NULL); if (pthread_sigmask(SIG_BLOCK, &set, NULL)) { fprintf(stderr, "pthread_sigmask failed\n"); exit(EXIT_FAILURE); } } void *buggy_thread(void *param) { char *ptr = NULL; block_signal(); printf("Thread %lu created\n", pthread_self()); // Sleep for some random time { ... } printf("About to raise from %lu\n", pthread_self()); // Raise a SIGSEGV *ptr = 0; pthread_exit(NULL); } void *dispatcher(void *param) { sigset_t set; siginfo_t info; int sig; sigemptyset(&set); sigaddset(&set, SIGSEGV); for (;;) { sig = sigwaitinfo(&set, &info); if (sig == -1) fprintf(stderr, "sigwaitinfo failed\n"); else printf("Received signal SIGSEGV from %u\n", info.si_pid); } } int main() { int i; pthread_t tid; pthread_t disp_tid; block_signal(); if (pthread_create(&disp_tid, NULL, dispatcher, NULL)) { fprintf(stderr, "Cannot create dispatcher\n"); exit(EXIT_FAILURE); } for (i = 0; i < 10; ++i) { if (pthread_create(&tid, NULL, buggy_thread, NULL) { fprintf(stderr, "Cannot create thread\n"); exit(EXIT_FAILURE); } } pause(); } ``` Unexpectedly, the program dies with a segmentation fault instead of printing the raiser's thread id.