当前位置: 首页 > 面试题库 >

如何编写信号处理程序以捕捉SIGSEGV?

严昀
2023-03-14
问题内容

我想编写一个信号处理程序来捕捉SIGSEGV。我保护一块内存以供使用

char *buffer;
char *p;
char a;
int pagesize = 4096;

mprotect(buffer,pagesize,PROT_NONE)

这样可以保护从缓冲区开始的内存的页面大小字节免受任何读取或写入的影响。

其次,我尝试读取内存:

p = buffer;
a = *p

这将生成一个SIGSEGV,并且将调用我的处理程序。到目前为止,一切都很好。我的问题是,调用处理程序后,我想通过以下方式更改内存的访问写入:

mprotect(buffer,pagesize,PROT_READ);

并继续正常运行我的代码。我不想退出该功能。在将来对同一内存进行写操作时,我想再次捕获该信号并修改写权限,然后记录该事件。

这是代码:

#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

char *buffer;
int flag=0;

static void handler(int sig, siginfo_t *si, void *unused)
{
    printf("Got SIGSEGV at address: 0x%lx\n",(long) si->si_addr);
    printf("Implements the handler only\n");
    flag=1;
    //exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    char *p; char a;
    int pagesize;
    struct sigaction sa;

    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = handler;
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
        handle_error("sigaction");

    pagesize=4096;

    /* Allocate a buffer aligned on a page boundary;
       initial protection is PROT_READ | PROT_WRITE */

    buffer = memalign(pagesize, 4 * pagesize);
    if (buffer == NULL)
        handle_error("memalign");

    printf("Start of region:        0x%lx\n", (long) buffer);
    printf("Start of region:        0x%lx\n", (long) buffer+pagesize);
    printf("Start of region:        0x%lx\n", (long) buffer+2*pagesize);
    printf("Start of region:        0x%lx\n", (long) buffer+3*pagesize);
    //if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
    if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
        handle_error("mprotect");

    //for (p = buffer ; ; )
    if(flag==0)
    {
        p = buffer+pagesize/2;
        printf("It comes here before reading memory\n");
        a = *p; //trying to read the memory
        printf("It comes here after reading memory\n");
    }
    else
    {
        if (mprotect(buffer + pagesize * 0, pagesize,PROT_READ) == -1)
        handle_error("mprotect");
        a = *p;
        printf("Now i can read the memory\n");

    }
/*  for (p = buffer;p<=buffer+4*pagesize ;p++ ) 
    {
        //a = *(p);
        *(p) = 'a';
        printf("Writing at address %p\n",p);

    }*/

    printf("Loop completed\n");     /* Should never happen */
    exit(EXIT_SUCCESS);
}

问题是只有信号处理程序运行,捕获信号后我无法返回到main函数


问题答案:

当您的信号处理程序返回时(假设它不调用exit或longjmp或阻止其实际返回的操作),代码将在发生信号时继续执行,重新执行同一指令。由于此时,内存保护尚未更改,它只会再次抛出信号,并且您将陷入无限循环中的信号处理程序中。

因此,要使其工作,您必须在信号处理程序中调用mprotect。不幸的是,正如Steven
Schansker指出的那样,mprotect不是异步安全的,因此您不能从信号处理程序中安全地调用它。因此,就POSIX而言,您很困惑。

幸运的是,对于大多数实现(据我所知,它是所有现代UNIX和Linux变体),mprotect是系统调用,因此可以从信号处理程序中安全地调用,因此您可以执行大部分所需的操作。问题是,如果您想在读取后重新更改保护,则必须在读取后在主程序中执行此操作。

另一种可能性是对信号处理程序的第三个参数进行操作,该参数指向一个操作系统和特定结构,该结构包含有关信号发生位置的信息。在Linux上,这是_ucontext_结构,其中包含有关$PC地址和发生信号的其他寄存器内容的机器特定信息。如果对此进行了修改,则可以更改信号处理程序将返回的位置,因此可以将$PC更改为仅在错误指令之后,以便它在处理程序返回后不会重新执行。要做到这一点非常棘手(也是不可携带的)。

编辑

所述ucontext结构定义<ucontext.h>。内的ucontext所述字段uc_mcontext包含了机器上下文,并在
中,阵列gregs包含通用寄存器上下文。因此,在您的信号处理程序中:

ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];

将为您提供发生异常的电脑。您可以阅读它以找出错误的指令,然后执行其他操作。

就在信号处理程序中调用mprotect的可移植性而言,遵循SVID规范或BSD4规范的任何系统都应该是安全的-
它们允许在信号中调用任何系统调用(手册第2节中的任何内容)处理程序。



 类似资料:
  • 4. 捕捉信号 4.1. 内核如何实现信号的捕捉 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达

  • 问题内容: 我需要在接收到任何终止命令(如SIGTERM和SIGKILL)时写入日志文件。 我可以注册SIGTERM,但是如何处理SIGKILL信号? 问题答案: 您不能,至少不是因为进程被杀死。 您 可以 做的是安排父进程监视子进程的死亡,并采取相应的措施。任何体面的过程监控系统(例如daemontools)都内置了这样的工具。

  • 问题内容: 我向用户展示了一个,但我不知道如何编写处理程序。这是我的尝试: 我在Xcode中遇到很多问题。 该文件说 目前,整个块/封盖都让我有些头疼。任何建议都非常感谢。 问题答案: 而不是将自己放在处理程序中,而应放置(提示:UIAlertAction!)。这应该使您的代码看起来像这样 这是在Swift中定义处理程序的正确方法。 正如Brian在下面指出的那样,还有更简单的方法来定义这些处理程

  • 我正在开发一个用PHP编写的预分叉TCP套接字服务器。 守护进程(父进程)分叉一定数量的子进程,然后等待,直到它被告知退出并且子进程都走了或者它收到信号。 SIGINT和SIGTERM使其将SIGTERM发送给所有子级。 孩子们建立了他们自己的信号处理器:SIGTERM导致一个干净的退出。SIGUSR1导致它转储一些状态信息(只需在下面的示例代码中打印出它收到了信号)。 如果子异常退出,则父级启动

  • 问题内容: 我想知道如何为在Swift 3中创建的函数创建完成处理程序。这就是我在更新到Swift 3之前就完成函数的方式: 但是现在我无法找出目前可行的最佳方法。 问题答案: 在Swift 3中,闭包中的函数参数标签不见了。 删除所有出现并添加 并使用它

  • 问题内容: 由于不是可重入的,因此在信号处理程序中使用它并不安全。但是我看过很多使用这种方式的示例代码。 所以我的问题是:我们什么时候需要避免在信号处理程序中使用,并且有推荐的替代品吗? 问题答案: 您可以使用一些标志变量,在信号处理程序中设置该标志,并在正常操作期间基于main()或程序其他部分中的该标志调用函数。 从信号处理程序中调用所有函数(例如)是不安全的。一种有用的技术是使用信号处理程序