Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Reading time: 7 minutes
tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Diese Seite fasst einen in-the-wild Adreno A7xx Microcode-Logikfehler (CVE-2025-21479) in reproduzierbare Exploit-Techniken zusammen: Ausnutzung der IB-Level-Maskierung in Set Draw State (SDS), um privilegierte GPU-Pakete aus einer unprivilegierten App auszuführen, Pivot auf GPU SMMU takeover und anschließend zu schnellem, stabilem Kernel R/W via 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).
Hintergrund: IB levels, SDS and the $12 mask
- Der Kernel verwaltet einen Ringpuffer (RB=IB0). Userspace übergibt IB1 via CP_INDIRECT_BUFFER, wobei Verkettungen zu IB2/IB3 möglich sind.
- SDS ist ein spezieller Kommando-Stream, der via CP_SET_DRAW_STATE betreten wird:
- A6xx: SDS wird als IB3 behandelt
- A7xx: SDS wurde auf IB4 verschoben
- Der Microcode verfolgt das aktuelle IB-Level im Register $12 und lässt privilegierte Pakete nur passieren, wenn das effektive Level IB0 (kernel RB) entspricht.
- Bug: A7xx microcode maskierte $12 weiterhin mit 0x3 (2 bits) statt mit 0x7 (3 bits). Da IB4 & 0x3 == 0, wurde SDS fälschlich als IB0 identifiziert, was privilegierte Pakete aus user-controlled SDS zuließ.
Warum das wichtig ist:
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-Beispiel (Patch änderte die Maske auf 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
Exploitation overview
Ziel: Von SDS (fälschlicherweise als IB0 gelesen) privilegierte CP-Pakete senden, um die GPU SMMU auf vom Angreifer erstellte Pagetabellen umzuleiten, und dann GPU copy/write-Pakete für beliebiges physisches R/W verwenden. Schließlich auf ein schnelles CPU-seitiges R/W via dirty pagetable pivoten.
High-level chain
- Eine gefälschte GPU Pagetabelle im shared memory erstellen
- SDS betreten und ausführen:
- CP_SMMU_TABLE_UPDATE -> auf gefälschte Pagetabelle umschalten
- CP_MEM_WRITE / CP_MEM_TO_MEM -> Write/Read-Primitiven implementieren
- CP_SET_DRAW_STATE mit run-now Flags (sofortige Ausführung)
GPU R/W primitives via fake pagetable
- Write: CP_MEM_WRITE an eine vom Angreifer gewählte GPU VA, deren PTEs du auf eine gewählte PA mapst -> beliebiger physischer Schreibzugriff
- Read: CP_MEM_TO_MEM kopiert 4/8 Bytes von der Ziel-PA in einen userspace-geteilten Puffer (Batch für größere Reads)
Notes
- Jeder Android-Prozess erhält einen KGSL-Kontext (IOCTL_KGSL_GPU_CONTEXT_CREATE). Das Wechseln der Kontexte aktualisiert normalerweise die SMMU-Tabellen im RB; der Bug erlaubt es, dies in SDS zu tun.
- Übermäßiger GPU-Traffic kann UI-Ausfälle und Reboots verursachen; Lesevorgänge sind klein (4/8B) und Sync ist standardmäßig langsam.
Building the SDS command sequence
- Eine gefälschte GPU-Pagetabelle in den shared memory sprühen, sodass mindestens eine Instanz an einer bekannten physischen Adresse landet (z. B. via allocator grooming und Wiederholung).
- Einen SDS-Buffer konstruieren, der in folgender Reihenfolge enthält:
- CP_SMMU_TABLE_UPDATE auf die physische Adresse der gefälschten Pagetabelle
- Ein oder mehrere CP_MEM_WRITE und/oder CP_MEM_TO_MEM Pakete, um R/W mittels deiner neuen Adress-Übersetzungen zu implementieren
- CP_SET_DRAW_STATE mit Flags, um sofort auszuführen
Die genauen Packet-Encodings variieren je nach Firmware; benutze freedreno’s afuc/packet docs, um die Worte zusammenzusetzen, und stelle sicher, dass der SDS-Submission-Pfad vom Treiber genommen wird.
Finding Samsung kernel physbase under physical KASLR
Samsung randomisiert die physische Kernel-Basis innerhalb eines bekannten Bereichs auf Snapdragon-Geräten. Brute-force den erwarteten Bereich und suche nach den ersten 16 Bytes von _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;
}
}
Sobald physbase bekannt ist, berechne das kernel virtual mit der linearen Abbildung:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabilisierung auf schnellen, zuverlässigen CPU-seitigen Kernel R/W (dirty pagetable)
GPU R/W ist langsam und hat eine geringe Granularität. Wechsle zu einer schnellen/stabilen Primitive, indem du deine eigenen Prozess-PTEs korrumpierst („dirty pagetable“):
Steps
- Finde current task_struct -> mm_struct -> mm_struct->pgd mithilfe der langsamen GPU R/W-Primitives
- mmap zwei benachbarte userspace pages A und B (z. B. bei 0x1000)
- Durchlaufe PGD->PMD->PTE, um die physischen Adressen der PTEs von A/B aufzulösen (Hilfsfunktionen: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Überschreibe B’s PTE, damit sie auf die last-level pagetable zeigt, die A/B verwaltet, mit RW-Attributen (phys_to_readwrite_pte)
- Schreibe über B’s VA, um A’s PTE zu verändern, sodass es Ziel-PFNs abbildet; lese/schreibe Kernel-Speicher über A’s VA und flush TLB, bis ein Sentinel umschlägt
Beispiel dirty-pagetable pivot snippet
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();
Detection and hardening
- Firmware/microcode: alle Stellen, die $12 maskieren, so ändern, dass 0x7 verwendet wird (A7xx) und privileged packet gates auditieren
- Driver: den effektiven IB-Level für privileged packets validieren und per-Kontext allowlists durchsetzen
- Telemetry: Alarm auslösen, wenn CP_SMMU_TABLE_UPDATE (oder ähnliche privileged opcodes) außerhalb von RB/IB0 erscheint, besonders in SDS; anomale Bursts von 4/8-Byte CP_MEM_TO_MEM und übermäßige TLB-flush-Muster überwachen
- Kernel: pagetable metadata härten und Muster von user PTE-Korruption erkennen
Impact
Eine lokale App mit GPU-Zugriff kann privileged GPU packets ausführen, die GPU SMMU übernehmen, beliebige kernel physical/virtual R/W erreichen, SELinux deaktivieren und auf betroffenen Snapdragon A7xx-Geräten (z. B. Samsung S23) root erlangen. Severity: High (kernel compromise).
References
- 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
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
HackTricks