Adreno A7xx SDS->RB Privileg-Bypass (GPU SMMU Übernahme zu Kernel R/W)

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Diese Seite abstrahiert einen in freier Wildbahn entdeckten Adreno A7xx microcode Logik-Bug (CVE-2025-21479) in reproduzierbare Exploitation-Techniken: Ausnutzung der IB-level Maskierung in Set Draw State (SDS), um privileged GPU packets aus einer unprivilegierten App auszuführen, Pivot zu GPU SMMU takeover und anschließend zu schnellem, stabilem Kernel R/W mittels eines dirty-pagetable Tricks.

  • 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).

Hintergrund: IB levels, SDS und die $12-Maske

  • Der Kernel hält einen Ringpuffer (RB=IB0). Userspace submits IB1 via CP_INDIRECT_BUFFER, chaining to IB2/IB3.
  • SDS ist ein spezieller Befehlsstrom, der via CP_SET_DRAW_STATE betreten wird:
  • A6xx: SDS wird als IB3 behandelt
  • A7xx: SDS wurde zu IB4 verschoben
  • Microcode verfolgt das aktuelle IB-Level im Register $12 und lässt privileged packets nur dann zu, wenn das effektive Level IB0 (kernel RB) entspricht.
  • Bug: A7xx microcode maskierte $12 weiterhin mit 0x3 (2 Bits) statt mit 0x7 (3 Bits). Da IB4 & 0x3 == 0, wurde SDS fälschlicherweise als IB0 identifiziert, was privileged packets aus einem von Benutzern kontrollierten SDS erlaubte.

Warum das wichtig ist:

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-Beispiel (Patch setzte die Maske auf 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

Exploitation overview

Ziel: Über SDS (fälschlich als IB0 gelesen) privilegierte CP-Pakete senden, um die GPU SMMU auf vom Angreifer erstellte Seitentabellen umzuleiten, dann GPU copy/write-Pakete für beliebiges physikalisches R/W verwenden. Schließlich zu einem schnellen CPU-seitigen R/W über dirty pagetable pivotieren.

High-level chain

  • Craft a fake GPU pagetable in shared memory
  • Enter SDS and execute:
  • CP_SMMU_TABLE_UPDATE -> switch to fake pagetable
  • CP_MEM_WRITE / CP_MEM_TO_MEM -> implement write/read primitives
  • 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 -> arbitrary physical write
  • 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.

Building the SDS command sequence

  • 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.

Finding Samsung kernel physbase under physical KASLR

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.

Representative loop

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;
}
}

Sobald physbase bekannt ist, berechne die Kernel-virtuelle Adresse mit der linearen Abbildung:

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

Auf schnelles, zuverlässiges CPU-seitiges Kernel R/W stabilisieren (dirty pagetable)

GPU R/W ist langsam und feingranular. Wechsle zu einem schnellen, stabilen Primitiv, indem du die PTEs deines eigenen Prozesses korrumpierst („dirty pagetable“):

Steps

  • Locate current task_struct -> mm_struct -> mm_struct->pgd using the slow GPU R/W primitives
  • mmap zwei benachbarte Userspace-Seiten A und B (z. B. bei 0x1000)
  • Durchlaufe PGD->PMD->PTE, um die physischen Adressen der PTEs von A/B zu ermitteln (Hilfsfunktionen: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Überschreibe B’s PTE so, dass sie auf die letzte Ebenen-Pagetabelle zeigt, die A/B verwaltet, mit RW-Attributen (phys_to_readwrite_pte)
  • Schreibe über B’s VA, um A’s PTE zu verändern und Ziel-PFNs zu mappen; Lese/schreibe Kernel-Speicher über A’s VA und flush TLB, bis ein sentinel umschaltet
Beispiel 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>

## Erkennung

- Telemetrie: Alarm auslösen, wenn CP_SMMU_TABLE_UPDATE (oder ähnliche privilegierte Opcodes) außerhalb von RB/IB0 erscheint, besonders in SDS; überwache anomale Bursts von 4/8-Byte CP_MEM_TO_MEM und übermäßige TLB-Flush-Muster

## Auswirkung

Eine lokale App mit GPU-Zugriff kann privilegierte GPU-Pakete ausführen, die GPU SMMU kapern, beliebige kernel physical/virtual R/W erreichen, SELinux deaktivieren und auf betroffenen Snapdragon A7xx-Geräten (z. B. Samsung S23) Root erlangen. Schwere: Hoch (Kernel-Kompromittierung).

### Siehe auch

<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>

## Referenzen

- [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]
> Lernen & üben Sie AWS Hacking:<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;">\
> Lernen & üben Sie GCP Hacking: <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;">
> Lernen & üben Sie Azure Hacking: <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>Unterstützen Sie HackTricks</summary>
>
> - Überprüfen Sie die [**Abonnementpläne**](https://github.com/sponsors/carlospolop)!
> - **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegram-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Teilen Sie Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repos senden.
>
> </details>