Segfault in SIGSEGV handler

自古美人都是妖i 提交于 2019-12-08 08:00:08

问题


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.


回答1:


From the man page for sigaction(2):

#include <signal.h>

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.




回答2:


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.



来源:https://stackoverflow.com/questions/33743599/segfault-in-sigsegv-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!