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

使用write(/dev/null)检查虚拟地址有效性触发段错误,求原因

虚拟地址有效性检查:write触发段错误的原因分析

尝试通过向/dev/null写入目标地址的方式检查虚拟地址有效性,却触发段错误;改用mincore函数则可正常运行,运行环境为Ubuntu 22.04.5 LTS x86_64,内核版本5.15.0-100-generic。

测试代码(触发段错误)

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int isMemoryMapped(void* addr) {
    int dev_null = open("/dev/null", O_WRONLY);
    if (dev_null == -1) {
        perror("Failed to open /dev/null");
        exit(EXIT_FAILURE);
    }

    errno = 0;
    // 尝试向/dev/null写入
    ssize_t result = write(dev_null, addr, sizeof(void*)); // <--- 地址无效时触发段错误
    close(dev_null);
    return (result != -1 && errno != EFAULT); // 可访问返回1,不可访问返回0
}

int main() {
    int a = 10, b = 20, c = 30;
    void* ptrArr[] = {&a, &b, &c};

    printf("Starting address of ptrArr: %p\n", ptrArr);

    for (int i = 0; i < 1000000; i++) {
        if (isMemoryMapped(&ptrArr[i])) {
            printf("%d : Address %p is accessible, content = %p\n", i, &ptrArr[i], ptrArr[i]);
        } else {
            printf("%d : Address %p is NOT accessible\n", i, &ptrArr[i]);
            break;
        }
    }
    return 0;
}

运行报错信息

...
1197 : Address 0x7ffeea336fe8 is accessible, content = 0x6f6d656d5f656661
1198 : Address 0x7ffeea336ff0 is accessible, content = 0x6e6163735f7972
1199 : Address 0x7ffeea336ff8 is accessible, content = (nil)
Segmentation fault (core dumped)

问题原因

  • write调用的本质是读取用户缓冲区内容:当传入完全未映射的无效虚拟地址时,CPU会立即触发页错误。内核处理该错误时,发现地址不属于进程的合法地址空间,会直接向进程发送SIGSEGV信号,导致段错误,根本不会走到设置errno=EFAULT并返回-1的逻辑。
  • 原方案仅适用于已映射但不可读的地址(比如写保护页):这种场景下,内核会拦截页错误,返回EFAULT而不杀死进程,但完全未映射的地址不在此范围内。
  • mincore的安全机制:mincore是专门用于查询虚拟地址映射状态的系统调用,它直接查询进程页表,不会尝试访问地址对应的物理内存。即使地址未映射,它只会返回错误码,不会触发页错误或发送信号,因此能安全完成检查。

可正常工作的mincore实现

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

int isMemoryMapped(void* addr) {
    // 将地址对齐到页边界
    long pageSize = sysconf(_SC_PAGESIZE);
    void* pageAlignedAddr = (void*)((uintptr_t)addr & ~(pageSize - 1));

    unsigned char vec;
    if (mincore(pageAlignedAddr, pageSize, &vec) == 0) {
        return 1;  // 内存已映射
    }
    return 0;  // 内存未映射
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:14:53