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

Linux平台下标记已分配内存检测非预期访问的方法

解决方案:Linux下标记内存空洞为不可访问的方法

以下是在Linux平台、GCC/Clang环境下,无需依赖Address Sanitizer即可标记内存空洞、触发类似段错误检测的实用方法:

1. 使用mmap + mprotect(推荐,直接触发段错误)

这是最贴合需求的方案:通过内存映射分配连续内存,将空洞区域设置为不可访问权限,一旦访问就会触发SIGSEGV信号(即段错误),完全模拟系统级内存越界行为。

核心原理

mmap可分配页对齐的连续内存块,mprotect能修改指定内存区域的访问权限。将空洞区域设为PROT_NONE后,任何读/写操作都会触发内核抛出段错误。

实现示例

#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    size_t page_size = sysconf(_SC_PAGESIZE);
    // 分配2页连续匿名内存(第一页正常使用,第二页作为空洞)
    void *mem = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
                     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (mem == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    // 将第二页(空洞区域)标记为不可访问
    if (mprotect((char*)mem + page_size, page_size, PROT_NONE) == -1) {
        perror("mprotect failed");
        munmap(mem, 2 * page_size);
        return 1;
    }

    // 正常访问测试(无异常)
    *(int*)mem = 42;
    printf("Normal access: %d\n", *(int*)mem);

    // 访问空洞区域(直接触发段错误)
    *(int*)((char*)mem + page_size) = 100;

    munmap(mem, 2 * page_size);
    return 0;
}

注意事项

  • 必须操作页对齐的内存:mprotect的起始地址和长度都必须是页大小的整数倍。如果你的空洞无法对齐到页边界,调试模式下可将包含空洞的整个页设为不可访问(牺牲少量正常内存,换取错误检测能力)。
  • 匿名映射(MAP_ANONYMOUS)无需关联文件,适合临时内存分配场景。

2. 使用posix_memalign + mprotect(适配malloc风格分配)

如果你更习惯类似malloc的内存分配方式,可以用posix_memalign分配页对齐内存,再通过mprotect标记空洞。

核心原理

posix_memalign支持指定对齐大小(需为页大小的倍数),分配后用mprotect修改权限的逻辑与上述方案一致。

实现示例

#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    size_t page_size = sysconf(_SC_PAGESIZE);
    void *mem;
    // 分配2页大小、页对齐的内存
    if (posix_memalign(&mem, page_size, 2 * page_size) != 0) {
        perror("posix_memalign failed");
        return 1;
    }

    // 标记第二页为不可访问
    if (mprotect((char*)mem + page_size, page_size, PROT_NONE) == -1) {
        perror("mprotect failed");
        free(mem);
        return 1;
    }

    // 正常访问测试
    *(int*)mem = 42;
    printf("Normal access: %d\n", *(int*)mem);

    // 访问空洞触发段错误
    *(int*)((char*)mem + page_size) = 100;

    free(mem);
    return 0;
}

注意事项

  • posix_memalign分配的内存需用free释放,而非munmap。
  • 对齐参数必须是2的幂且不小于sizeof(void*),直接使用页大小即可满足需求。

3. 调试模式填充特殊值+主动检查(轻量替代)

如果页对齐限制对你的数据结构不友好,可在调试模式下给空洞填充特殊标记值,通过主动检查检测意外访问。

核心思路

  • 调试模式初始化时,将空洞区域填充0xDEADBEEF或0xBAADF00D这类特殊值。
  • 在关键操作节点(如数据结构修改后、函数返回前)遍历空洞区域,检查标记值是否被篡改。
  • 若发现篡改,立即触发段错误或终止程序。

实现示例

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>

#define DEBUG 1

typedef struct {
    int data[10];
    // 假设该区域为永久空洞
    char hole[100];
    int more_data[5];
} MyStruct;

#ifdef DEBUG
#define HOLE_MARKER 0xDEADBEEF

void check_hole(MyStruct *s) {
    for (int i = 0; i < sizeof(s->hole)/sizeof(int); i++) {
        if (*((int*)&s->hole[i*sizeof(int)]) != HOLE_MARKER) {
            fprintf(stderr, "Hole accessed unexpectedly!\n");
            raise(SIGSEGV); // 触发段错误
        }
    }
}
#endif

int main() {
    MyStruct s;
    memset(&s, 0, sizeof(s));
#ifdef DEBUG
    // 填充空洞标记值
    memset(s.hole, HOLE_MARKER, sizeof(s.hole));
#endif

    // 正常操作
    s.data[0] = 42;
    s.more_data[0] = 100;

#ifdef DEBUG
    check_hole(&s);
#endif

    // 模拟越界写操作(调试模式下会被检测到)
    s.hole[0] = 'a';

#ifdef DEBUG
    check_hole(&s);
#endif

    return 0;
}

优缺点

  • 优点:无需页对齐,适配任意大小的空洞。
  • 缺点:无法实时触发错误,需主动检查;仅能检测写操作,无法捕获读访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:26:12