SIGSEGV处理程序中的Segfault

时间:2015-11-16 20:01:20

标签: c unit-testing segmentation-fault posix

假设我有以下C代码:

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 

}

如果执行遇到第一个分段错误,则会触发处理程序。但是,当处理程序本身由于某种原因发生分段错误时会发生什么?它是否会使用新的siginfo_t再次调用处理程序,还是会立即终止程序?

如果再次调用处理程序,则可能会起作用:

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);
}

我之所以要问的是我目前正在用C编写某种单元测试框架。如果测试用例由于某些非法的内存访问而失败,我想将所有剩余的测试标记为失败。如果由于某种原因,测试已经严重搞砸了堆,这个操作可能会失败,因为我必须遍历数据结构并将该信息写入xml文件。但是如果将所有剩余的测试标记为失败,我仍然希望保留测试失败的信息。

2 个答案:

答案 0 :(得分:2)

来自sigaction(2)的手册页:

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction
    *oldact);  

....
     

sigaction结构定义为

          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给出了一个应该被阻止的信号掩码   执行信号处理程序。另外,信号当中   触发处理程序将被阻止,除非SA_NODEFER标志是   使用

因为在设置信号处理程序时没有设置SA_NODEFER标志,如果在信号处理程序中发生另一个段错误,则不会再次调用它>。退出后,之前被阻止的信号将被传递。

答案 1 :(得分:1)

至少Linux会使用SIGSEGV的默认信号处理程序,如果它出现在SIGSEGV处理程序中,请参阅kernel/signal.c,这样就不会再次调用你的处理程序。所引用的代码似乎仅在内核尝试为信号处理程序设置堆栈帧时发生段错误时使用(例如,因为堆栈指针无效)。

当您的segfault处理程序发生段错误时,您的程序将处于非常破碎的状态,我不会依赖任何东西并使用一些处理该情况的包装器。

除此之外,要符合POSIX标准,您不能在信号处理程序中使用printf或类似的东西。对于您可以使用的功能,请检查man 7 signal(寻找安全功能)。 write()位于该列表中,因此您可以使用它编写自己的输出函数。