IPBUF安全漏洞报告
English
CVE-2025-39961 CVSS 4.7 中危

CVE-2025-39961 Linux内核AMD IOMMU页表级提升竞态条件漏洞

披露日期: 2025-10-09
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2025-39961
漏洞类型
竞态条件(Race Condition)
CVSS评分
4.7 中危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel (AMD IOMMU驱动 iommu/amd/pgtbl)

相关标签

竞态条件Linux KernelAMD IOMMU页表管理本地提权拒绝服务内核漏洞CWE-362DMA安全IOVA管理

漏洞概述

CVE-2025-39961是Linux内核AMD IOMMU主机页表实现中的一个竞态条件漏洞。AMD IOMMU主机页表实现支持动态页表级别(最多6级),从3级配置开始,根据IOVA地址进行扩展。内核维护一个根指针和当前页表级别,以便在alloc_pte()/fetch_pte()操作中正确执行页表遍历。当IOMMU IOVA分配器耗尽32位地址空间时,会切换到64位地址模式,此时AMD IOMMU驱动程序需要增加页表级别以支持更大的IOVA空间。然而,在unmap路径(iommu_v1_unmap_pages())中,fetch_pte()在不加锁的情况下读取pgtable->[root/mode],这可能导致在极端情况下,当increase_address_space()正在更新pgtable->[root/mode]时,fetch_pte()读取到错误的页表级别(pgtable->mode),使其与页表中编码的级别进行比较后返回NULL,最终导致iommu_unmap操作失败,上层可能触发重试或记录WARN_ON警告。该漏洞的CVSS评分为4.7,属于中危级别,主要影响系统可用性,攻击者需要本地低权限访问即可触发。

技术细节

该漏洞的核心技术原理在于AMD IOMMU页表管理中的并发读写竞态问题。具体而言:

1. **页表结构**:AMD IOMMU使用多级页表(最多6级),初始为3级。当IOVA地址空间从32位扩展到64位时,需要增加页表级别。内核通过pgtable->root(根指针)和pgtable->mode(当前页表级别)来管理页表状态。

2. **竞态窗口**:在increase_address_space()函数中,代码先更新pgtable->root为新的根页表值,然后再更新pgtable->mode(pgtable->mode += 1)。这两个操作之间存在时间窗口。

3. **竞态触发**:与此同时,在unmap路径中,iommu_v1_unmap_pages()调用fetch_pte()读取pgtable->[mode/root]时没有加锁。当CPU 0执行map操作触发increase_address_space(),CPU 1同时执行unmap操作调用fetch_pte()时,可能出现以下情况:fetch_pte()读取到新的root值但旧的mode值,导致模式不匹配而返回NULL。

4. **影响后果**:fetch_pte()返回NULL会导致iommu_unmap操作失败,上层可能进行重试或触发WARN_ON警告,在极端情况下可能导致系统不稳定或拒绝服务。

5. **修复方案**:由于页表级别更新操作频率较低且已经使用自旋锁同步,开发者采用seqcount机制来实现读路径的无锁读取,从而避免竞态条件。

攻击链分析

STEP 1
步骤1
攻击者获取本地低权限访问权限,在启用AMD IOMMU的系统上运行恶意程序
STEP 2
步骤2
通过大量DMA映射操作触发IOMMU IOVA分配器耗尽32位地址空间,促使系统切换到64位地址模式
STEP 3
步骤3
increase_address_space()开始更新页表级别,先更新pgtable->root,再更新pgtable->mode
STEP 4
步骤4
与此同时,攻击者线程触发unmap操作,fetch_pte()在不加锁的情况下读取到新root但旧mode
STEP 5
步骤5
fetch_pte()因模式不匹配返回NULL,导致iommu_unmap操作失败
STEP 6
步骤6
上层驱动进行重试或触发WARN_ON警告,可能导致系统不稳定、性能下降或拒绝服务

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
/* * CVE-2025-39961 PoC - Race condition trigger in AMD IOMMU pgtbl * This PoC demonstrates the race condition between increase_address_space() * and fetch_pte() in the AMD IOMMU page table implementation. * * Note: This is a conceptual PoC. Actual exploitation requires kernel-level * access and specific IOMMU configuration. */ #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <sys/mman.h> #define NUM_THREADS 2 #define ITERATIONS 10000 /* Simulated shared page table state */ volatile unsigned long pgtable_root = 0x1000; volatile int pgtable_mode = 3; /* Start with 3-level page table */ volatile int race_detected = 0; /* * Simulates increase_address_space() - CPU 0 (map path) * Updates root first, then mode - creating the race window */ void *cpu0_map_thread(void *arg) { for (int i = 0; i < ITERATIONS; i++) { /* Simulate alloc_pte() -> increase_address_space() */ pgtable_root = 0x2000 + i; /* Update root to new value */ /* Small delay to widen the race window */ for (volatile int j = 0; j < 10; j++); pgtable_mode = 4; /* Update mode after root */ } return NULL; } /* * Simulates iommu_v1_unmap_pages() -> fetch_pte() - CPU 1 (unmap path) * Reads root and mode without lock - detects inconsistency */ void *cpu1_unmap_thread(void *arg) { unsigned long local_root; int local_mode; for (int i = 0; i < ITERATIONS; i++) { local_root = pgtable_root; /* Read root without lock */ local_mode = pgtable_mode; /* Read mode without lock */ /* Check for inconsistency: new root with old mode */ if (local_root >= 0x2000 && local_mode == 3) { race_detected++; fprintf(stderr, "[RACE DETECTED] root=0x%lx mode=%d " "(new root with old mode)\n", local_root, local_mode); } } return NULL; } int main(int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; printf("CVE-2025-39961 PoC: AMD IOMMU pgtbl race condition\n"); printf("Starting %d threads to trigger race...\n", NUM_THREADS); /* Create threads to simulate concurrent map/unmap operations */ pthread_create(&threads[0], NULL, cpu0_map_thread, NULL); pthread_create(&threads[1], NULL, cpu1_unmap_thread, NULL); /* Wait for both threads */ pthread_join(threads[0], NULL); pthread_join(threads[1], NULL); printf("\nResults: %d race conditions detected\n", race_detected); if (race_detected > 0) { printf("Vulnerability confirmed: race condition exists in " "page table level update path\n"); return 1; } printf("No race detected in this run. Try increasing ITERATIONS.\n"); return 0; }

影响范围

Linux Kernel < 6.17 (包含AMD IOMMU驱动iommu/amd/pgtbl的版本)

防御指南

临时缓解措施
在无法立即升级内核的情况下,可以考虑以下临时缓解措施:1) 限制本地用户对IOMMU相关设备的访问权限;2) 监控系统日志中是否出现WARN_ON或iommu_unmap失败的相关警告;3) 减少并发DMA映射/解映射操作的频率以降低触发竞态的概率;4) 如果BIOS支持,可以临时禁用AMD IOMMU以规避问题(但会降低I/O虚拟化安全性)。

参考链接

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