Adreno A7xx SDS->RB 权限绕过 (GPU SMMU takeover to Kernel R/W)

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 微代码逻辑漏洞 (CVE-2025-21479) 抽象为可复现的利用技术:滥用 Set Draw State (SDS) 中的 IB-level masking 以从非特权应用执行特权 GPU packets,pivot 到 GPU SMMU takeover,然后通过 dirty-pagetable trick 快速、稳定地获得 kernel R/W。

  • Affected: Qualcomm Adreno A7xx GPU firmware prior to a microcode fix that changed masking of register $12 from 0x3 to 0x7.
  • Primitive: Execute privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) from SDS, which is user-controlled.
  • Outcome: Arbitrary physical/virtual kernel memory R/W, SELinux disable, root.
  • Prereq: Ability to create a KGSL GPU context and submit command buffers that enter SDS (normal app capability).

Background: IB levels, SDS and the $12 mask

  • The kernel maintains a ringbuffer (RB=IB0). Userspace submits IB1 via CP_INDIRECT_BUFFER, chaining to IB2/IB3.
  • SDS is a special command stream entered via CP_SET_DRAW_STATE:
  • A6xx: SDS is treated as IB3
  • A7xx: SDS moved to IB4
  • Microcode tracks the current IB level in register $12 and gates privileged packets so they are only accepted when the effective level corresponds to IB0 (kernel RB).
  • Bug: A7xx microcode kept masking $12 with 0x3 (2 bits) instead of 0x7 (3 bits). Since IB4 & 0x3 == 0, SDS was misidentified as IB0, allowing privileged packets from user-controlled SDS.

Why it matters:

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 示例 (补丁将掩码切换为 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

利用概览

Goal: 从 SDS (misread as IB0) 下发特权 CP packets 来将 GPU SMMU 重新指向攻击者构造的 page tables,然后使用 GPU copy/write packets 实现任意物理 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 with run-now flags(立即 dispatch)

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

  • 写:CP_MEM_WRITE 到攻击者选择的 GPU VA,该 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 buffer,按顺序包含:
  1. CP_SMMU_TABLE_UPDATE 指向假的 pagetable 的物理地址
  2. 一个或多个 CP_MEM_WRITE 和/或 CP_MEM_TO_MEM 包以使用新的映射实现 R/W
  3. 带有 run-now 标志的 CP_SET_DRAW_STATE

确切的包编码随固件而异;使用 freedreno’s afuc/packet docs 来组装 words,并确保驱动走的是 SDS 提交路径。

在 physical KASLR 下定位 Samsung kernel physbase

Samsung 在 Snapdragon 设备上会在已知区域内随机化内核物理基址。对预期范围进行暴力搜索,并查找 _stext 的前 16 个字节。

示例循环

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 端 kernel R/W(dirty pagetable)

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

Steps

  • Locate current task_struct -> mm_struct -> mm_struct->pgd using the slow GPU R/W primitives
  • mmap 两个相邻的 userspace 页面 A 和 B(例如在 0x1000)
  • Walk PGD->PMD->PTE 来解析 A/B 的 PTE 物理地址(helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Overwrite B’s PTE to point to the last-level pagetable managing A/B with RW attributes (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();

</details>

## 检测

- 遥测:如果 CP_SMMU_TABLE_UPDATE(或类似的特权 opcodes)出现在 RB/IB0 之外,尤其是在 SDS 中,则发出警报;监控异常的 4/8 字节 CP_MEM_TO_MEM 突发和过量的 TLB 刷新模式

## 影响

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

### See also

<a class="content_ref" href="pixel-bigwave-bigo-job-timeout-uaf-kernel-write.md"><span class="content_ref_label">Pixel Bigwave Bigo Job Timeout Uaf Kernel Write</span></a>

## References

- [CVE-2025-21479: Adreno A7xx SDS->RB privilege bypass to kernel R/W (Samsung S23)](https://xploitbengineer.github.io/CVE-2025-21479)
- [Mesa freedreno afuc disassembler README (microcode + packets)](https://gitlab.freedesktop.org/mesa/mesa/-/blob/c0f56fc64cad946d5c4fda509ef3056994c183d9/src/freedreno/afuc/README.rst)
- [Google Project Zero: Attacking Qualcomm Adreno GPU (SMMU takeover via CP packets)](https://googleprojectzero.blogspot.com/2020/09/attacking-qualcomm-adreno-gpu.html)
- [Dirty pagetable (archive)](https://web.archive.org/web/20240425043203/https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html)

> [!TIP]
> 学习和实践 AWS 黑客技术:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> 学习和实践 GCP 黑客技术:<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> 学习和实践 Azure 黑客技术:<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>支持 HackTricks</summary>
>
> - 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)!
> - **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](https://t.me/peass) 或 **在** **Twitter** 🐦 **上关注我们** [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub 仓库提交 PR 来分享黑客技巧。
>
> </details>