Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Hierdie bladsy abstraheer ’n in-die-wild Adreno A7xx microcode logiese fout (CVE-2025-21479) in reproduceerbare exploitation-tegnieke: misbruik van IB-level masking in Set Draw State (SDS) om privileged GPU packets van ’n nie-geprivilegieerde app uit te voer, pivot na GPU SMMU takeover en dan na ’n vinnige, stabiele kernel R/W deur ’n dirty-pagetable truuk.

  • Geaffekteer: Qualcomm Adreno A7xx GPU firmware voor ’n microcode-fix wat die maskering van register $12 van 0x3 na 0x7 verander het.
  • Primitief: Voer privileged CP packets (bv. CP_SMMU_TABLE_UPDATE) uit vanaf SDS, wat deur die gebruiker beheer word.
  • Uitkoms: Willekeurige fisiese/virtuele kernel-geheue R/W, SELinux deaktiveer, root.
  • Vereiste: Vermoë om ’n KGSL GPU context te skep en command buffers in te dien wat SDS betree (normale app-vaardigheid).

Agtergrond: IB levels, SDS and the $12 mask

  • Die kernel onderhou ’n ringbuffer (RB=IB0). Userspace dien IB1 in via CP_INDIRECT_BUFFER, ketting na IB2/IB3.
  • SDS is ’n spesiale command stream wat via CP_SET_DRAW_STATE ingegaan word:
  • A6xx: SDS is treated as IB3
  • A7xx: SDS moved to IB4
  • Die microcode hou die huidige IB-vlak by in register $12 en laat privileged packets slegs toe wanneer die effektiewe vlak ooreenstem met IB0 (kernel RB).
  • Fout: A7xx microcode het $12 met 0x3 (2 bits) gemasker in plaas van 0x7 (3 bits). Aangesien IB4 & 0x3 == 0, is SDS verkeerd geïdentifiseer as IB0, wat privileged packets van deur gebruiker beheerde SDS toegelaat het.

Hoekom dit saak maak:

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 voorbeeld (patch het die mask na 0x7 gestel):

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

Oorsig van uitbuiting

Doel: Vanuit SDS (verkeerd gelees as IB0) uitgereikte bevoorregte CP-pakkette om die GPU SMMU na attacker-crafted page tables te herlei, en dan GPU copy/write-pakkette te gebruik vir arbitrêre fisiese R/W. Laastens pivot na ’n vinnige CPU-side R/W via dirty pagetable.

Hoëvlak ketting

  • Skep ’n valse GPU pagetable in gedeelde geheue
  • Gaan in SDS en voer uit:
  • CP_SMMU_TABLE_UPDATE -> skakel na die valse pagetable
  • CP_MEM_WRITE / CP_MEM_TO_MEM -> implementeer write/read primitives
  • CP_SET_DRAW_STATE with run-now flags (stuur onmiddellik)

GPU R/W-primitiewe via valse pagetable

  • Skryf: CP_MEM_WRITE na ’n deur aanvaller-gekose GPU VA waarvan die PTEs jy map na ’n gekose PA -> arbitrêre fisiese skryf
  • Lees: CP_MEM_TO_MEM kopieer 4/8 bytes vanaf teiken PA na ’n userspace-gedeelde buffer (batch vir groter reads)

Aantekeninge

  • Elke Android-proses kry ’n KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Om contexts te skakel werk normaalweg die SMMU-tabelle in die RB by; die bug laat jou toe om dit in SDS te doen.
  • Oormatige GPU traffic kan UI blackouts en reboots veroorsaak; reads is klein (4/8B) en sync is standaard stadig.

Bou die SDS-opdragreeks

  • Spray ’n valse GPU pagetable in gedeelde geheue sodat minstens een instansie op ’n bekende fisiese adres land (bv. via allocator grooming en herhaling).
  • Konstrueer ’n SDS-buffer wat, in volgorde, bevat:
  1. CP_SMMU_TABLE_UPDATE na die fisiese adres van die valse pagetable
  2. Een of meer CP_MEM_WRITE en/of CP_MEM_TO_MEM pakkette om R/W te implementeer met jou nuwe translations
  3. CP_SET_DRAW_STATE with flags to run-now

Die presiese packet encodings verskil per firmware; gebruik freedreno’s afuc/packet docs om die words te assembleer, en verseker dat die SDS submission path deur die driver geneem word.

Vind Samsung kernel physbase onder fisiese KASLR

Samsung randomiseer die kernel fisiese basis binne ’n bekende streek op Snapdragon devices. Brute-force die verwagte reeks en soek die eerste 16 bytes van _stext.

Voorbeeld-lus

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

Sodra physbase bekend is, bereken die kernel virtuele met die lineêre kaart:

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

Stabiliseer na vinnige, betroubare CPU-side kernel R/W (dirty pagetable)

GPU R/W is stadig en van fyn granulariteit. Skuif na ’n vinnige/betroubare primitive deur jou eie proses PTEs te korrupteer (“dirty pagetable”):

Stappe

  • Lokaliseer die huidige task_struct -> mm_struct -> mm_struct->pgd met behulp van die stadige GPU R/W primitives
  • mmap twee aangrensende userspace pages A en B (bv., by 0x1000)
  • Loop PGD->PMD->PTE om A/B se PTE fisiese adresse op te los (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Oorskryf B se PTE om na die laaste-vlak pagetable wat A/B bestuur te wys, met RW-attribuute (phys_to_readwrite_pte)
  • Skryf via B se VA om A se PTE te verander om target PFNs te map; lees/skryf kernel-geheue via A se VA, spoel die TLB totdat ’n sentinel omskakel
Voorbeeld 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>

## Opsporing en verharding

- Firmware/microcode: herstel alle sites wat $12 maskeer om 0x7 te gebruik (A7xx) en oudit privileged packet gates
- Driver: valideer die effektiewe IB-vlak vir privileged packets en afdwing per-konteks allowlists
- Telemetry: waarsku indien CP_SMMU_TABLE_UPDATE (of soortgelyke privileged opcodes) buite RB/IB0 verskyn, veral in SDS; moniteer anomalieë van 4/8-byte CP_MEM_TO_MEM en oormatige TLB-flush patrone
- Kernel: verhard pagetable metadata en detecteer user PTE-korrupsiepatrone

## Impak

'n Lokale app met GPU-toegang kan privileged GPU packets uitvoer, die GPU SMMU kaap, arbitrêre kernel fisiese/virtuele R/W bereik, SELinux deaktiveer en root kry op aangetas Snapdragon A7xx-toestelle (bv. Samsung S23). Ernstigheid: Hoog (kernel kompromie).

## 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]
> Leer en oefen 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;">\
> Leer en oefen 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;">
> Leer en oefen 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>Ondersteun HackTricks</summary>
>
> - Kyk na die [**subskripsie planne**](https://github.com/sponsors/carlospolop)!
> - **Sluit aan by die** 💬 [**Discord groep**](https://discord.gg/hRep4RUj7f) of die [**telegram groep**](https://t.me/peass) of **volg** ons op **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Deel hacking truuks deur PRs in te dien na die** [**HackTricks**](https://github.com/carlospolop/hacktricks) en [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
>
> </details>