C SIGSEGV 处理程序和保护

发布于 2024-08-30 07:17:35 字数 695 浏览 11 评论 0原文

我正在构建一个程序,它使用 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 等)?

I'm constructing a program which uses mprotect() to restrict a block of memory from accessing. When the memory is requested, a SIGSEGV is thrown which I listen for using a signal() call.

Once the SIGSEGV has been detected, I need to somehow access the pointer to the memory that was requested (that threw the fault) and the size of the segment requested. Is this possible?

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);
}

Additionally, is there a way to determine the level of mprotect() a block of memory is currently assigned (PROT_NONE,PROT_READ, etc..)?

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

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

发布评论

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

评论(3

離殇 2024-09-06 07:17:35

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

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

You have to use sigaction with SA_SIGINFO instead of signal to establish your handler, and then you will get called back with useful information in a siginfo_t, including si_addr.

si_addr, as explained in sigaction(2), will contain the address. As for the length, well, you're out of luck unless you're willing to parse instructions. Best you can do is take action for the page reported in si_addr, and then if that's not enough, you'll get another signal soon enough. At least, that's how we did things in ObjectStore.

↘紸啶 2024-09-06 07:17:35

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

但要注意,调用 mprotect 只是Linux 中的信号安全,其他 POSIX 系统可能不支持这一点。

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

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

You are looking for libsigsegv
http://libsigsegv.sourceforge.net/

But beware that calling mprotect is only signal-safe in Linux, other POSIX systems may not support this.

I am afraid that in Linux the only way to get memory protection bits is to read in /proc/$pid/meminfo

On a side note (Linux only): If you are worried about memory consumption and intend to enable pages of a larger mapping one by one then I would advise to create your mapping using mmap with MAP_NORESERVE in which case you will get a mapping to zero-filled copy-on-write pages which will allocate physical RAM on the first write. MAP_NORESERVE instructs the kernel not to back your memory with swap space allowing you to allocate up to 64TB of virtual address space. Only downside is that if you do run out of memory terrible things can happen (oom-killer).

口干舌燥 2024-09-06 07:17:35

第 1 步:初始化 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);

根据需要添加错误处理

第 4 步:定义处理函数:

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 的手册页,了解处理程序可以提取的更多有趣数据。

Step 1: Init a 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;

Step 2: Make this sigaction handle SIGSEGV:

sigaction(SIGSEGV, &act, NULL);

(Optional) Step 3: Make it handle other memory signals too:

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

Add error handling as necessary

Step 4: Define the handler function:

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
}

You can refer to the man pages for ucontext_t and siginfo_t for more interesting data your handler can extract.

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