Linux内核v6.9(x86_64)空指针解引用触发SIGSEGV的相关疑问
x86_64 Linux内核v6.9中空指针解引用的处理流程与疑问解答
背景概述
针对x86_64架构下Linux内核v6.9,分析以下两段空指针解引用代码的内核内部处理流程:
// 空指针解引用+写入页零 *(char *)0 = 0; // 空指针解引用+读取页零 char c = *(char *)0;
通过Ftrace追踪得到调用链:
handle_mm_fault <-- do_user_addr_fault sanitize_fault_flags <-- handle_mm_fault arch_vma_access_permitted <-- handle_mm_fault bad_area_nosemaphore <-- do_user_addr_fault __bad_area_nosemaphore <-- do_user_addr_fault force_sig_fault <-- __bad_area_nosemaphore
代码触发页错误后,arch_vma_access_permitted()或sanitize_fault_flags()会返回VM_FAULT_SIGSEGV,随后__bad_area_nosemaphore()通过force_sig_fault()向进程发送SIGSEGV信号。以下是具体疑问的解答:
疑问解答
1. 页零的权限是什么?它默认是否会被映射?若未映射,是否由vma_is_foreign()触发段错误?
- 默认情况下,页零(虚拟地址0开始的4KB页)不会被映射。内核为了捕获空指针解引用这类错误,特意保留虚拟地址空间起始页不做映射。
- 当进程访问未映射的页零地址时,会触发页错误并进入
do_user_addr_fault流程。由于找不到对应VMA(虚拟内存区域),会走到bad_area_nosemaphore分支,最终通过force_sig_fault发送SIGSEGV信号。vma_is_foreign()主要用于处理跨进程VMA访问场景,并非触发页零未映射导致段错误的核心逻辑——核心原因是该地址无对应VMA,内核无法完成页映射,因此判定为非法访问。
2. 在load_elf_binary()中,为兼容旧版Linux ABI的SVr4行为,当current->personality包含MMAP_PAGE_ZERO时,会以PROT_READ|PROT_EXEC权限映射页零,为何需要PROT_EXEC权限?此时读取页零是否不会触发段错误?
- 添加
PROT_EXEC权限是为了严格兼容SVr4 ABI的历史行为:早期SVr4系统中,页零可能包含初始化代码或跳转逻辑,需要可执行权限才能正常运行依赖该ABI的旧程序。 - 当页零被以
PROT_READ|PROT_EXEC映射后,读取页零不会触发段错误,但写入操作依然会触发SIGSEGV(因为映射权限未包含PROT_WRITE)。此时进程对页零的读取访问会被内核正常处理,不会触发页错误。
3. MMAP_PAGE_ZERO特性何时会应用于任务?默认情况下除该场景外,是否还有其他映射页零的逻辑?
MMAP_PAGE_ZERO特性仅在加载符合特定条件的ELF二进制时触发:当ELF程序的personality标记包含MMAP_PAGE_ZERO(通常是为兼容旧版SVr4 ABI的程序),load_elf_binary()会主动映射页零。- 默认情况下,除上述兼容旧ABI的场景外,内核没有其他自动映射页零的逻辑。用户进程也可通过
mmap系统调用手动映射虚拟地址0,但需满足两个条件:一是进程的personality允许(默认不允许,需通过personality(ADDR_NO_RANDOMIZE)等方式调整);二是内核参数vm.mmap_min_addr被设置为0(默认值为4096,禁止映射低地址以防范空指针漏洞利用)。
内容的提问来源于stack exchange,提问作者Iman Seyed
相关产品推荐
相关产品推荐

