【问题标题】:C SIGSEGV Handler & MprotectC SIGSEGV 处理程序和 Mprotect
【发布时间】:2011-02-11 22:39:07
【问题描述】:

我正在构建一个程序,它使用 mprotect() 来限制访问一块内存。当请求内存时,会抛出一个 SIGSEGV,我使用 signal() 调用来监听它。

一旦检测到 SIGSEGV,我需要以某种方式访问​​指向所请求内存(引发故障)的指针和所请求段的大小。这可能吗?

void fifoSigHandler(){

    // Needs to only remove protection from requested block of virtual memory
    mprotect(fifoVm,(size_t)fifoVm_size,PROT_WRITE);
    printf("Caught Seg Fault");
}

void fifo_init(void* vm, int vm_size, int n_frames, int page_size)
{
    fifoVm = vm;
    fifoVm_size = vm_size;
    fifoFrames = n_frames;
    fifoPageSize = page_size;

    mprotect(fifoVm,(size_t)fifoVm_size,PROT_NONE);

    signal(SIGSEGV, fifoSigHandler);
}

另外,有没有办法确定当前分配的内存块的 mprotect() 级别(PROT_NONE、PROT_READ 等)?

【问题讨论】:

    标签: signals handler segmentation-fault mprotect


    【解决方案1】:

    您必须使用sigactionSA_SIGINFO 而不是signal 来建立您的处理程序,然后您将在siginfo_t 中收到有用信息的回调,包括si_addr

    si_addr,如sigaction(2) 中所述,将包含地址。至于长度,除非你愿意解析指令,否则你就不走运了。你能做的最好的就是对si_addr 中报告的页面采取行动,然后如果这还不够,你很快就会收到另一个信号。至少,我们在 ObjectStore 中是这样处理的。

    【讨论】:

    • 我们如何参考这些有用的信息?特别是请求内存的指针和大小
    【解决方案2】:

    您正在寻找libsigsegv http://libsigsegv.sourceforge.net/

    但请注意,调用mprotect 仅在 Linux 中是信号安全的,其他 POSIX 系统可能不支持。

    恐怕在Linux中获得内存保护位的唯一方法是读入/proc/$pid/meminfo

    附带说明(仅限 Linux):如果您担心内存消耗并打算逐个启用较大映射的页面,那么我建议您使用 mmapMAP_NORESERVE 创建映射您将获得到零填充的写时复制页面的映射,该页面将在第一次写入时分配物理 RAM。 MAP_NORESERVE 指示内核不要使用交换空间支持您的内存,从而允许您分配多达 64TB 的虚拟地址空间。唯一的缺点是,如果你的内存不足,可能会发生可怕的事情(oom-killer)。

    【讨论】:

      【解决方案3】:

      第一步:初始化一个sigaction

      struct sigaction act;
      memset(&act, 0, sizeof(struct sigaction));
      sigemptyset(&act.sa_mask);
      act.sa_sigaction = handler;
      act.sa_flags = SA_SIGINFO | SA_ONSTACK;
      

      第 2 步:让这个sigaction 处理SIGSEGV

      sigaction(SIGSEGV, &act, NULL);
      

      (可选)第 3 步:让它也处理其他内存信号:

      sigaction(SIGBUS, &act, NULL);
      sigaction(SIGTRAP, &act, NULL);
      

      根据需要添加错误处理

      第四步:定义处理函数:

      void handler(int signal, siginfo_t* siginfo, void* uap) {
          printf("Attempt to access memory at address %p\n", 
                 siginfo->si_addr);
          #ifdef LINUX_64BIT
          printf("Instruction pointer: %p\n",
                 (((ucontext_t*)uap)->uc_mcontext.gregs[16]));
          #elif LINUX_32BIT
          printf("Instruction pointer: %p\n",
                 (((ucontext_t*)uap)->uc_mcontext.gregs[14]));
          #endif
      }
      

      您可以参考ucontext_tsiginfo_t 的手册页,了解您的处理程序可以提取的更多有趣数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-02
        • 1970-01-01
        • 1970-01-01
        • 2020-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-12
        相关资源
        最近更新 更多