【问题标题】:Is there a way to pre-emptively avoid a segfault?有没有办法先发制人地避免段错误?
【发布时间】:2011-05-16 22:40:36
【问题描述】:

情况如下:

我正在通过使用挂钩 ioctl() 系统调用的 LD_PRELOADed 模块来分析程序与驱动程序的交互。我正在使用的系统(嵌入式 Linux 2.6.18 内核)幸运地具有编码到“请求”参数中的数据长度,因此我可以愉快地转储具有正确长度的 ioctl 数据。

但是,很多这些数据都有指向其他结构的指针,我不知道这些数据的长度(毕竟这是我正在调查的内容)。所以我正在扫描数据中的指针,并将数据转储到那个位置。我担心如果指针靠近段边界,这可能会使我的代码对段错误开放(我的早期测试似乎表明情况如此)。

所以我想知道在尝试取消引用之前我可以做些什么来先发制人地检查当前进程是否拥有特定的偏移量?这甚至可能吗?

编辑:只是一个更新,因为我忘了提到一些可能非常重要的东西,目标系统是基于 MIPS 的,尽管我也在我的 x86 机器上测试我的模块。

【问题讨论】:

标签: c pointers segmentation-fault dereference


【解决方案1】:

打开/dev/null 的文件描述符并尝试write(null_fd, ptr, size)。如果它返回 -1 并将 errno 设置为 EFAULT,则内存无效。如果它返回size,则内存是可以安全读取的。使用一些 POSIX 发明可能有一种更优雅的方式来查询内存有效性/权限,但这是经典的简单方式。

【讨论】:

    【解决方案2】:

    如果您的嵌入式 linux 安装了 /proc/ 文件系统,您可以解析 /proc/self/maps 文件并验证指针/偏移量。 maps文件包含进程的内存映射,见here

    【讨论】:

    • 感谢您的建议,起初这似乎很完美,但在考虑如何实现时,我意识到它可能相当慢,因为每次调用 ioctl 时我都必须解析它。
    【解决方案3】:

    我不知道这种可能性。但是您也许可以实现类似的目标。正如man 7 signal 提到的,SIGSEGV 可以被抓到。因此,我认为你可以

    1. 从取消引用已知为指针的字节序列开始
    2. 逐个访问一个字节,有时会触发SIGSEGV
    3. SIGSEGV的处理程序中,标记在步骤2的循环中检查的变量
    4. 退出循环,此页面完成。

    这有几个问题。

    • 由于多个缓冲区可能存在于同一页中,因此您可能会输出您认为是一个缓冲区的内容,但实际上是多个缓冲区。您也许也可以通过LD_PRELOADing 电子围栏来帮助解决这个问题,AFAIK 会导致应用程序为每个动态分配的缓冲区分配整个页面。所以你不会输出几个缓冲区,认为它只有一个,但你仍然不知道缓冲区在哪里结束,最后会输出很多垃圾。此外,此方法无法帮助基于堆栈的缓冲区。
    • 您不知道缓冲区在哪里结束。

    未经测试。

    【讨论】:

    • 如果进程已经有 SIGSEGV 的信号处理程序,这将如何实现?我不确定它是否存在,但这是一种明显的可能性。信号处理程序可以分层吗?不知道缓冲区在哪里结束并不是什么大问题,对于有趣的人,我会知道它有多长,我只需要一种“包罗万象”的方法来暂时转储数据。
    • signal(2) 返回指向先前安装的处理程序的指针。在取消引用调用它之前,您应该检查常量 SIG_IGNSIG_DFL。如果你想在非 Linux 的 Unices 上使用它,那么使用 sigaction(2) 可能是值得的。它还允许查询以前安装的 sigaction。
    【解决方案4】:

    您不能只检查段边界吗? (我猜段边界是指页面边界?)

    如果是这样,页面边界被很好地分隔(4K 或 8K),所以简单的地址掩码应该可以处理它。

    【讨论】:

      猜你喜欢
      • 2013-04-06
      • 1970-01-01
      • 2019-09-07
      • 1970-01-01
      • 2019-05-29
      • 2011-09-13
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      相关资源
      最近更新 更多