【问题标题】:IsBadReadPtr analogue on UnixUnix 上的 IsBadReadPtr 类似物
【发布时间】:2011-01-06 04:36:23
【问题描述】:

在 Unix 中是否有类似于 IsBadReadPtr 的函数?至少 IsBadReadPtr 的一些功能? 我想编写一个程序,如果某个进程(如SIGSEGV)发生不好的事情会做出反应并恢复一些信息。但我想检查指针以确保数据没有损坏,并查看它们是否可以安全访问。否则崩溃处理程序本身就会崩溃,从而变得无用。

有什么建议吗?

【问题讨论】:

标签: c debugging unix libc


【解决方案1】:

在 POSIX 系统上执行此操作的常用方法是使用 write() 系统调用。如果无法读取内存,它将在errno 中返回EFAULT,而不是发出信号:

int nullfd = open("/dev/random", O_WRONLY);

if (write(nullfd, pointer, size) < 0)
{
    /* Not OK */
}
close(nullfd);

(/dev/random 在 Linux 上是一个很好的设备,因为它可以由任何用户编写,并且实际上会尝试读取给定的内存。在没有 /dev/random 或它不可写的操作系统上,试试/dev/null)。另一种选择是匿名管道,但如果您想测试一个大区域,您需要定期清除管道的读取端。

【讨论】:

  • 此解决方案不适用于 Linux 3.8.7 ... write() 如果 'fd' 为 /dev/null,即使 'pointer' 为 NULL,总是返回成功。
  • 在 Linux 上,您可以使用 /dev/random - 我已经更新了答案。
  • /dev/random write() 修改熵池是 POSIX 的事情还是 Linux 的事情?如果由于某种原因或在某些平台上没有效果,那么内核可以选择跳过写操作并立即返回成功,就像使用 /dev/null 作为优化一样。如果没有这些保证,我仍然会对那个解决方案感到紧张。
  • 另外,如果它确实修改了熵池,写入 /dev/random 可能会很慢。我最终使用的是通过 shm_open() 的 POSIX 共享内存对象。为了控制文件/对象的大小,只要近似大小超过某个阈值,我就会执行 lseek(shm_fd, 0, SEEK_SET) 。这样做(连同其他一些优化)是为了将摊销的开销限制为每个 isMemoryBlockReadable(void*, size_t) 调用一个系统调用。
  • 我不确定/dev/random 本身是否在 POSIX 中。共享内存对象方法也可能是一个好主意,但请注意,POSIX 将write()(和lseek())对共享内存对象的影响“未指定”。我怀疑最便携的解决方案是pipe() 文件描述符,但这需要定期读取或关闭/重新创建管道。
【解决方案2】:

在 Windows 或 Unix 上,您永远无法判断“是否可以安全地访问指针”。但对于某些 unix 平台上的一些类似信息,请查看cat /proc/self/maps

【讨论】:

  • 如果我在 gdb 下运行,我可以显示一个内存位置,gdb 会显示一些东西或者告诉我"Cannot access memory at address blah-blah"。所以它知道我是否可以访问地址 blah-blah - 我想知道如何从我的程序中做同样的事情。
  • @StasM:gdb 做出了很好的猜测。它还通过 ptrace poke 访问 another 进程中的内存,所以如果它崩溃了也没关系。
【解决方案3】:

你是怎么做到的?

您尝试这样做,然后处理错误。

为此,首先设置一个 sigsetjmp 和一个 SIGSEGV 信号处理程序。然后尝试使用指针。如果它是一个错误的指针,则调用 SIGSEGV 处理程序,您可以跳转到安全位置并向用户报告错误。

【讨论】:

  • 如果我尝试在 SIGSEGV 处理程序中执行它会变得有点复杂......也可以是 SIGBUS。
【解决方案4】:

我在从虚拟机中运行 Ubuntu 时尝试从帧缓冲区读取“像素”时遇到了同样的问题。似乎没有安全的方法来检查访问而不崩溃或 acutally 挂起 gdb。 StasM 提出的建议暗示我要遵循使用 fork 的“本地”解决方案。

void *some_address;
int pid = fork();
if (pid== 0)
{
    someaddress[0] = some_address[0];
    _exit(123);
}
bool access_ok = true;
int status;
int result = waitpid(pid, &status, 0);
if (result == -1 || WIFEXITED(status) == 0 || WEXITSTATUS(status) != 123)
{
    access_ok = false;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-12
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-03
    相关资源
    最近更新 更多