You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在信号处理程序中获取触发地址并修改内存页权限?

能否在SIGSEGV信号处理程序中获取触发地址并修改页权限?

当然可以!当你通过SA_SIGINFO标志注册信号处理函数时,内核会通过siginfo_t结构体传递触发SIGSEGV的关键信息——其中**si_addr字段就存储了导致内存访问错误的具体地址**。你完全可以利用这个地址定位到对应的内存页,再通过mprotect()修改该页的权限,从而避免信号反复触发。

关键细节说明

  • 只有当信号处理函数是通过sa_sigaction(而非sa_handler)注册,且设置了SA_SIGINFO标志时,才能拿到包含si_addr的siginfo_t结构体。
  • 对mprotect()的调用需要操作整个内存页,所以你需要把si_addr对齐到所在页的起始地址(可以用页大小做位运算实现)。
  • 信号处理函数内尽量使用异步信号安全的系统调用(比如write()),避免调用非线程安全的函数(比如printf()),不过示例中为了可读性暂时保留了printf,实际生产环境建议替换。

修改后的代码示例

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

static void hdl(int sig, siginfo_t *siginfo, void *context) {
    printf("Caught Signal %d\n", sig);
    if (sig == SIGSEGV) {
        // 获取触发SIGSEGV的内存地址
        void *fault_addr = siginfo->si_addr;
        printf("Fault occurred at address: %p\n", fault_addr);

        // 对齐到内存页起始地址
        long page_size = getpagesize();
        void *page_start = (void *)((unsigned long)fault_addr & ~(page_size - 1));

        // 修改该页权限为可读可写
        if (mprotect(page_start, page_size, PROT_READ | PROT_WRITE) == -1) {
            perror("mprotect failed");
            exit(EXIT_FAILURE);
        }
        printf("Modified page permissions for address: %p\n", page_start);
    }
}

int main() {
    struct sigaction act;
    memset(&act, '\0', sizeof(act));

    // 使用sa_sigaction并设置SA_SIGINFO标志,以获取siginfo_t信息
    act.sa_sigaction = &hdl;
    act.sa_flags = SA_SIGINFO;

    if (sigaction(SIGSEGV, &act, NULL) < 0) {
        perror("sigaction failed");
        return 1;
    }

    // 映射一个只读的匿名内存页
    int *ip = (int *)mmap(NULL, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (ip == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    // 尝试写入只读页,触发SIGSEGV
    *ip = 1;
    printf("Value after write: %d\n", *ip);

    // 释放映射的内存
    munmap(ip, getpagesize());
    return 0;
}

代码运行逻辑

  1. 程序先映射一个只读的匿名内存页。
  2. 当尝试向只读页写入数据时,触发SIGSEGV信号。
  3. 信号处理函数捕获信号,通过siginfo->si_addr拿到故障地址,对齐到页起始位置后,用mprotect()将该页权限改为可读可写。
  4. 信号处理完成后,程序回到触发故障的指令继续执行,此时写入操作已合法,不会再触发SIGSEGV。

内容的提问来源于stack exchange,提问作者Jamie Davenport

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:40:47