About c : Segfault-in-SIGSEGV-handler
Question Detail
Lets assume I have the following C code:
static void handler(int sig, siginfo_t *si, void *unused)
{
printf(“BOOM!\n”);
//another segfault here
exit(-1);
}
int main(int argc, char *argv[])
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror(“failed to set handler”);
// call other stuff here, somewhere in the callstack a segfault happens
}
If the execution hits the first segmentation fault the handler will be triggered. But what happens when in the handler itself from some reason a segmentation fault happens? Will it call the handler again with the new siginfo_t or will the program be terminated immediately?
If the handler is called again would something like this work:
int in_handler = 0;
static void handler(int sig, siginfo_t *si, void *unused)
{
printf(“BOOM!\n”);
if(!in_handler)
{
in_handler = 1;
//code that could possibly segfault
} else {
//totally safe code
}
exit(-1);
}
The reason why I’m asking is that I’m currently writing some sort of unit test framework in C. If a testcase failed due to some illegal memory access I would like to mark all remaining tests as failed. If for some reason the test hast screwed up the heap really badly this operation could fail because I have to iterate over a data structure and write that information to a xml file. But if marking all remaining tests as failed I would still like to preserver the information which test was the failing one.
Question Answer
From the man page for sigaction(2):
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction
*oldact);
….
The sigaction structure is defined as something like
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
….
sa_mask gives a mask of signals which should be blocked during
execu- tion of the signal handler. In addition, the signal which
triggered the handler will be blocked, unless the SA_NODEFER flag is
used.
Because you didn’t set the SA_NODEFER flag when setting up your signal handler, it won’t get called again if another segfault occurs while still in the signal handler. Once you exit, the signal which was previously blocked will then be delivered.
……………………………………………………
At least Linux will use the default signal handler for SIGSEGV if it occurs within the SIGSEGV handler, see kernel/signal.c, so your handler won’t be called again. The cited code seems only to be used when the segfault happens while the kernel tries to set up the stack frame for the signal handler (for example because the stack pointer is invalid).
As your program will be in a pretty broken state when your segfault handler segfaults, I wouldn’t rely on anything and use some wrapper that handles that case.
Apart from that, to be POSIX compliant, you can’t use printf or anything similar in your signal handler. For functions you can use, check man 7 signal (look for safe functions). write() is on that list so you can write your own output functions using that.