【问题标题】:Segfault in SIGSEGV handlerSIGSEGV 处理程序中的段错误
【发布时间】:2015-11-16 20:01:20
【问题描述】:

假设我有以下 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 文件。但是,如果将所有剩余的测试标记为失败,我仍然希望保留哪些测试是失败的信息。

【问题讨论】:

  • 我建议在分叉进程中运行每个测试,并在父进程中检索终止状态。这将允许您不丢弃所有剩余的测试,并能够明确报告子段错误。

标签: c unit-testing segmentation-fault posix


【解决方案1】:

来自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 标志,所以如果在信号处理程序中发生另一个段错误,它将不会再次被调用。一旦退出,之前被阻塞的信号就会被传递出去。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 看起来我错了,引用的代码做了一些不同的事情。至少在设置SA_NODEFER 时如dbush 指出的那样,在Linux 上再次调用信号处理程序。
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 2016-08-18
    • 2011-02-11
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多