【问题标题】:Is signal.h a reliable way to catch null pointers?signal.h 是捕获空指针的可靠方法吗?
【发布时间】:2023-03-06 04:08:01
【问题描述】:

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

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

它似乎有效(使用我非常简单的测试用例),但我没有找到任何保证 Sigsegv 被抛出 null-derefs 或处理程序被调用用于 OS 生成的信号。

所以我的问题是: 我可以指望现代 destkop 操作系统上的 signal.h 吗(我真的不在乎它是否不是标准的在 linux/win 以外的其他东西上不起作用:这是一个宠物项目)。是否有任何重要的事情我应该注意(signal(...) 或 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 处理,不应该发生。

【问题讨论】:

  • 除了取消引用空指针之外,代码还可以取消引用垃圾指针或任何其他类型的无效指针。这不一定会产生信号。因此,除了验证每个指针访问之外,您别无选择。
  • 如果您将指针取消引用操作传递给主机操作系统,则它不是 VM。充其量它是一个仿真包装器(尽管不要告诉 WINE 那就是它!)。
  • @Sam Varshavchik:这就是最后一个注释存在的原因:垃圾不应该发生:永远。
  • @OP:不,但您的担心似乎是处理一般的分段错误,而不仅仅是 NULL 指针。例如:*(int *)(-5) = 1 可能会产生段错误,即使它不包含任何 NULL 指针。

标签: c++ c error-handling operating-system signals


【解决方案1】:

我可以指望现代 destkop 操作系统上的 signal.h 吗

您可以信赖它,因为标头和其中的功能将在所有符合标准的系统上可用。但是,究竟什么信号被抛出以及何时在操作系统之间并不一致。

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


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

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

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

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

【讨论】:

  • 非常感谢您,先生。这就是我要找的!
【解决方案2】:

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

【讨论】:

  • 我读到处理函数不应该返回(因为它具有 undef 行为)。这是否意味着我必须手动清理对 printf 的调用? C++ IO也是这样吗?
  • 不只是 printf,它是此处未明确列出的任何标准库函数:man7.org/linux/man-pages/man7/signal.7.html。是的,它包括 C++ I/O,它是使用 C 函数实现的,它还包括更多的 I/O(再举一个例子,mktime() 不能使用)。
猜你喜欢
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-18
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 2015-11-15
相关资源
最近更新 更多