C++:使用 segvcatch 安全吗?

我刚刚偶然发现了 segvcatch 库,它承诺包装 segfaults并将浮点错误转换为适当的异常.

I just stumbled upon the segvcatch library which promises to wrap segfaults and floating point errors into appropriate exceptions.

使用这个库是安全的,如果我添加所有捕获的段错误将只是空指针访问的前提条件(即,没有数组溢出或无效指针可能在段错误之前完全搞砸内存,无论如何都会导致未定义的行为)?捕获 nullptr 段错误后,程序是否仍具有定义的语义?浮点错误呢?他们表现得更好/不同吗?

Is using this library safe, if I add the precondition that all segfaults caught will only be null pointer accesses (i.e., no array overflows or invalid pointers which could have screwed up the memory completely before segfaulting, resulting in undefined behaviour anyway)? Will the program still have defined semantics after catching a nullptr segfault? What about floating point errors? Do they behave better/different?

旁注:请不要发表任何评论,说明任何产生段错误的程序都是格式错误的,应该调试/修复.我知道,我同意.不过,我对这个问题很感兴趣.

Sidenote: Please no comments stating that any program producing a segfault is ill-formed anyway and should be debugged/fixed. I know that and I agree. Still, I am interested in this question.

推荐答案

不安全.

信号处理程序非常简单,但它们完全是错误的.这是 SEGV 处理程序:

The signal handlers are very simple, and they are simply wrong. Here's the SEGV handler:

void default_segv()
{
    throw std::runtime_error("Segmentation fault");
}

这是相当非法的,至少在 POSIX 上是这样.从信号处理程序中抛出异常是一个非常非常糟糕的主意.

That is quite illegal, at least on POSIX. Throwing an exception from within a signal handler is a very, very bad idea.

附录
那么为什么这是一个坏主意呢?

Addendum
So why is this a bad idea?

使用 SIGALRM,这比一个坏主意更糟糕;这是未定义的行为,因为警报是异步的.使用 SIGSEGV 和 SIGBUS 这是一个非常糟糕的主意.对于其他信号,这只是一个坏主意.有时它可能会起作用.其他时候,可能不会.当魔法不起作用时,结果可能是非常灾难性的.

With SIGALRM, it's worse than a bad idea; it's undefined behavior because alarms are asynchronous. With SIGSEGV and SIGBUS it's an extremely bad idea. With other signals, it's merely a bad idea. It might work, sometimes. Other times, it might not. Results can be quite disastrous when the magic doesn't work.

我先看看 SEGV.分段违规和总线错误的一个常见原因是破坏堆栈.如果这是信号的原因,则没有可展开的堆栈.throw 将尝试展开堆栈,这将引发另一个 SEGV.怎么办?在我的电脑上,这是一场灾难.

I'll look at SEGV first. One common cause of segmentation violations and bus errors is smashing the stack. There is no stack to unwind if this was the cause of the signal. The throw will try to unwind the stack, which will raise another SEGV. Now what? On my computer, that's a disaster.

不管信号如何,在信号处理程序中抛出对于 RAII 而言都是不安全的,因为在处理程序的上下文.从信号处理程序的上下文中调用大量的函数是不安全的.处理程序中 throw 之后发生的所有事情都是在信号处理程序的上下文中完成的,因为 throw 不会从处理程序返回.throw 绕过了 return.

Regardless of the signal, throwing inside a signal handler is not safe with respect to RAII because free() (and hence delete) is not safe to call within the context of a handler. There are a whole slew of functions that aren't safe to call from within the context of a signal handler. Everything that happens after the throw in the handler is done from within the context of the signal handler because the throw doesn't return from the handler. The throw bypasses the return.

那些不安全的调用和不安全的展开意味着可以在处理旧信号的同时引发新信号.这个递归信号是很成问题的.例如,在我的计算机上,信号变为 SIGSTOP.该程序不会退出,它不会丢弃核心.它只是挂在那里,永久冻结,直到我杀死它 -9 或重新启动机器.

Those unsafe calls and the unsafe unwinding means that a new signal can be raised while still handling that old signal. This recursive signal is highly problematic. On my computer, for example, the signal gets changed to SIGSTOP. The program doesn't exit, it doesn't drop core. It just hangs there, permanently frozen until I either kill -9 it or reboot the machine.

相关文章