Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Questa pagina astragge un bug di logica del microcode Adreno A7xx riscontrato in-the-wild (CVE-2025-21479) in tecniche di sfruttamento riproducibili: abusare del IB-level masking in Set Draw State (SDS) per eseguire privileged GPU packets da un’app non privilegiata, pivotare al GPU SMMU takeover e poi ottenere un kernel R/W veloce e stabile tramite dirty-pagetable trick.
- Interessati: Qualcomm Adreno A7xx GPU firmware precedente a una correzione del microcode che ha cambiato il masking del registro $12 da 0x3 a 0x7.
- Primitive: Execute privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) from SDS, which is user-controlled.
- Risultato: Arbitrary physical/virtual kernel memory R/W, SELinux disable, root.
- Prerequisito: Ability to create a KGSL GPU context and submit command buffers that enter SDS (normal app capability).
Contesto: IB levels, SDS and the $12 mask
- Il kernel mantiene un ringbuffer (RB=IB0). Userspace sottomette IB1 via CP_INDIRECT_BUFFER, concatenando a IB2/IB3.
- SDS è uno stream di comandi speciale entrato via CP_SET_DRAW_STATE:
- A6xx: SDS è trattato come IB3
- A7xx: SDS spostato a IB4
- Il microcode traccia il livello IB corrente nel registro $12 e filtra le privileged packets in modo che siano accettate solo quando il livello effettivo corrisponde a IB0 (kernel RB).
- Bug: Il microcode A7xx continuava a mascherare $12 con 0x3 (2 bit) invece di 0x7 (3 bit). Poiché IB4 & 0x3 == 0, SDS veniva identificato erroneamente come IB0, permettendo privileged packets da SDS controllato dall’utente.
Perché è importante:
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
Esempio di diff del microcode (la patch ha impostato la mask su 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
Panoramica dell’exploit
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.
Costruire la sequenza di comandi 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:
- 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.
Trovare il kernel physbase di Samsung sotto 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;
}
}
Una volta noto physbase, calcola l’indirizzo virtuale del kernel con la mappa lineare:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabilizzare un R/W kernel lato CPU veloce e affidabile (dirty pagetable)
GPU R/W è lento e a granularità ridotta. Passa a un primitivo veloce/affidabile corrompendo i PTEs del tuo processo (“dirty pagetable”):
Passaggi
- Localizza l’attuale task_struct -> mm_struct -> mm_struct->pgd usando i primitivi GPU R/W lenti
- mmap due pagine userspace adiacenti A e B (es., a 0x1000)
- Cammina PGD->PMD->PTE per risolvere gli indirizzi fisici dei PTE di A/B (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Sovrascrivi il PTE di B per puntare alla last-level pagetable che gestisce A/B con attributi RW (phys_to_readwrite_pte)
- Scrivi tramite la VA di B per mutare il PTE di A in modo da mappare i PFNs target; leggi/scrivi memoria kernel tramite la VA di A, flushando la TLB finché una sentinella non cambia
Esempio di snippet pivot dirty-pagetable
```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>
## Rilevamento
- Telemetria: alert se CP_SMMU_TABLE_UPDATE (or similar privileged opcodes) appare outside RB/IB0, especially in SDS; monitorare raffiche anomale di 4/8-byte CP_MEM_TO_MEM e pattern eccessivi di flush della TLB
## Impatto
Un'app locale con accesso alla GPU può eseguire privileged GPU packets, hijack the GPU SMMU, ottenere arbitrary kernel physical/virtual R/W, disabilitare SELinux e ottenere root sui dispositivi Snapdragon A7xx interessati (es., Samsung S23). Gravità: Alta (compromissione del kernel).
### Vedi anche
<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>
## Riferimenti
- [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]
> Impara e pratica il 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;">\
> Impara e pratica il 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;">
> Impara e pratica il 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>Supporta HackTricks</summary>
>
> - Controlla i [**piani di abbonamento**](https://github.com/sponsors/carlospolop)!
> - **Unisciti al** 💬 [**gruppo Discord**](https://discord.gg/hRep4RUj7f) o al [**gruppo telegram**](https://t.me/peass) o **seguici** su **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Condividi trucchi di hacking inviando PR ai** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repos github.
>
> </details>


