段错误之后:有没有办法检查指针是否仍然有效?

发布于 2024-10-06 20:52:18 字数 310 浏览 2 评论 0原文

我计划创建一个日志记录/跟踪机制,它将字符串文字的地址 (const char*) 写入环形缓冲区。这些字符串位于只读数据段中,由预处理器使用 __function____file__ 创建。

问题:如果所有指针都有效,是否可以在段错误后分析此环形缓冲区内容?对于“有效”,我的意思是它们指向映射的内存区域,并且取消引用不会导致分段错误。

我正在使用 Linux 2.6.3x 和 GCC 4.4.x。

最好的问候,

查理

I plan to create a logging/tracing mechanism, which writes the address (const char*) of string literals to a ring-buffer. These strings are in the read-only data-segment and are created by the preprocessor with __function__ or __file__.

The Question: Is it possible, to analyze this ring-buffer content after a Segfault, if all pointers are valid? With "valid" I mean that they point to a mapped memory area and dereferencing won't cause a segmentation fault.

I'm working with Linux 2.6.3x and GCC 4.4.x.

Best regards,

Charly

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

稳稳的幸福 2024-10-13 20:52:18

检查取消引用内存区域是否会导致段错误的常用方法是使用 read()write()。例如,检查 ptr 指向的前 128 个字节是否安全可读:(

int fd[2];
if (pipe(fd) >= 0) {
    if (write(fd[1], ptr, 128) > 0)
        /* OK */
    else
        /* not OK */
    close(fd[0]);
    close(fd[1]);
}

write() 将返回 EFAULT 而不是在以下情况下引发信号:该区域不可读)。

如果您想一次测试超过 PIPE_BUF 字节,则需要从管道的读取端读取并丢弃。

The usual way to check if dereferencing a memory region will cause a segfault is to use read() or write(). Eg to check if the first 128 bytes pointed to by ptr are safely readable:

int fd[2];
if (pipe(fd) >= 0) {
    if (write(fd[1], ptr, 128) > 0)
        /* OK */
    else
        /* not OK */
    close(fd[0]);
    close(fd[1]);
}

(write() will return EFAULT rather than raising a signal if the region isn't readable).

If you want to test more than PIPE_BUF bytes at a time, you'll need to read and discard from the reading side of the pipe.

蔚蓝源自深海 2024-10-13 20:52:18

我认为您正在寻找的方法是通过 处理 SIGSEGV 信号sigaction

void handler(int, siginfo_t *info, ucontext_t *uap)
{
   /* Peek at parameters here...  I'm not sure exactly what you want to do. */
}

/* Set up the signal handler... */

struct sigaction sa, old_sa;
memset(&sa, 0 sizeof(sa));

sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;

if (sigaction(SIGSEGV, &sa, &old_sa))
{
   /* TODO: handle error */
}

但请注意,在您自己的进程上捕获 SIGSEGV 有点奇怪。该进程可能处于无法恢复的不良状态。您可以采取的应对措施可能会受到限制,并且进程被终止很可能是一件好事。

如果您希望它更稳定一点,可以使用 sigaltstack 调用允许您指定备用堆栈缓冲区,这样,如果您完全清理了堆栈,您仍然可以处理 SIGSEGV。要使用此功能,您需要在上面的 sa.sa_flags 中设置 SA_ONSTACK

如果您想从另一个进程的安全性中响应 SEGV (从而将自己与表现不佳的段错误代码隔离开来,这样您在检查它时就不会崩溃),您可以使用 ptrace。该接口很复杂,有许多不可移植的部分,并且主要用于编写调试器。但是您可以用它做一些伟大的事情,例如读写进程的内存和寄存器,以及改变其执行。

I think the approach you are looking for is to handle a SIGSEGV signal via sigaction.

void handler(int, siginfo_t *info, ucontext_t *uap)
{
   /* Peek at parameters here...  I'm not sure exactly what you want to do. */
}

/* Set up the signal handler... */

struct sigaction sa, old_sa;
memset(&sa, 0 sizeof(sa));

sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;

if (sigaction(SIGSEGV, &sa, &old_sa))
{
   /* TODO: handle error */
}

Note however that catching SIGSEGV on your own process is kind of weird. The process is likely in a bad state that can't be recovered from. The actions you'll be able to do in response to it may be limited, and it's most likely the process being killed is a good thing.

If you want it to be a bit more stable, there is the sigaltstack call which lets you specify an alternate stack buffer, so that if you've completely hosed your stack you can still handle SIGSEGV. To use this you need to set SA_ONSTACK in sa.sa_flags above.

If you want to respond to SEGV from the safety of another process (thereby isolating yourself from the poorly behaving segfaulting code and making it so that you won't crash while inspecting it), you can use ptrace. This interface is complex, has many non-portable parts, and is mainly used to write debuggers. But you can do great things with it, like read and write the process's memory and registers, and alter its execution.

叹倦 2024-10-13 20:52:18

当然,如果您依赖的堆栈或其他内存已损坏,则可能会出现问题,但对于任何代码都是如此。

假设您所依赖的堆栈或其他内存没有问题,并且假设您没有调用任何非 async-signal safe,并假设您不尝试从信号处理程序返回,那么应该从信号处理程序中读取或写入缓冲区没有问题。

如果您尝试测试特定地址是否有效,可以使用系统调用,例如 mincore() 并检查错误结果。

Of course if the stack or other memory that you rely upon has been corrupted then there could be problems, but that is true for any code.

Assuming that that there is no problem with the stack or other memory that you rely upon, and assuming that you do not call any functions like malloc() that are not async-signal safe, and assuming that you do not attempt to return from your signal handler, then there should be no problem reading or writing your buffer from within your signal handler.

If you are trying to test whether a particular address is valid, you could use a system call such as mincore() and check for an error result.

那小子欠揍 2024-10-13 20:52:18

一旦收到段错误,所有的赌注都会被取消。指针可能有效,也可能已损坏。你只是不知道。您也许能够将它们与有效值进行比较,或者指向环形缓冲区本身的指针可能已损坏。在这种情况下,你可能会得到垃圾。

Once you've received a segfault, all bets are off. The pointers may be valid or they may have been corrupted. You just don't know. You may be able to compare them with valid values or the pointer to the ring buffer itself may have been corrupted. In which case, you'll probably get garbage.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文