信号.h 是捕获空指针的可靠方法吗?

Is signal.h a reliable way to catch null pointers?

本文关键字:方法 空指针 信号      更新时间:2023-10-16

我目前正在用C/C++编写一个小VM。显然,如果用户取消引用空指针,我不能让整个 VM 崩溃,所以我必须检查每个访问,随着 VM 的增长和更多系统的实施,这些访问变得越来越麻烦。

所以我有一个想法:为 sigsegv 编写一个信号处理程序,让操作系统做它的事情,而不是关闭程序,调用 VM 异常处理程序。

它似乎可以工作(使用我非常简单的测试用例),但我没有找到任何保证 Sigsegv 被抛出在 null-deref 上的东西,也没有为操作系统生成的信号调用处理程序。

所以我的问题是:我可以指望现代 destkop 操作系统上的 signal.h 吗(我真的不在乎它是否不是标准的,不适用于 linux/win 以外的其他东西:这是一个宠物项目)。我应该注意什么非平凡的事情吗(信号(...)或longjmp(...)的模糊限制?

谢谢!

这是伪实现:

/* ... */
jmp_buf env;
/* ... */
void handler(int) {
    longjmp(env, VM_NULLPTR);
}
/* ... */
if(setjmp(env)) {
    return vm_throw("NullPtrException");
} 
switch(opcode) {
    /* instructions */
    case INVOKE:
        *stack_top = vm_call(stack_top->obj); // don't check anything in the case where stack_top or stack_top->obj is null handler() will be called an a "NullPtrException" will be thrown
    break;
    /* more instructions */
}
/* ... */

注意:我只需要检查空值,垃圾(悬空)指针由GC处理,不应该发生。

仅当信号处理程序从未从异步信号不安全代码调用信号处理程序时,从信号处理程序调用longjmp()才是安全的。 因此,例如,如果您可能通过将错误的指针传递给任何printf()系列函数来接收 SIGSEGV,则无法从信号处理程序中longjmp()

我可以在现代 destkop 操作系统上依靠 signal.h 吗

从某种意义上说,您可以依靠它,因为标头及其中的功能将在所有符合标准的系统上可用。但是,究竟抛出什么信号以及何时抛出在操作系统中并不一致。

在Windows上,您可能需要使用cygwin或类似的环境编译程序,以使系统引发分段错误。使用 Visual Studio 编译的程序使用"结构化异常"来处理无效的存储访问。


signal.h 是捕获空指针的可靠方法吗?

在某些情况下,空指针取消引用不会导致引发分段错误信号,即使在 POSIX 系统上也是如此。

  • 一种情况可能是编译器优化了操作,例如,在取消引用 null 指针以调用不访问任何数据成员的成员函数的情况下,这是典型的情况。当没有无效的内存访问时,也没有信号。当然,在这种情况下也不会发生崩溃。
  • 另一种情况可能是地址 0 实际上是有效的。AIX 就是这种情况,您并不关心。在 Linux 上也是如此,您确实关心这种情况,但不是默认的,您可能会选择不关心它的情况。有关更多详细信息,请参阅此答案。

然后是信号处理程序的实现。 longjmp不是异步信号安全的,因此如果在执行另一个非安全操作时发出信号,则中断的操作可能使程序处于不一致状态。有关详细信息,请参阅 John Zwinck 的答案和 libc 文档。