Hacktricks-skills adreno-a7xx-exploitation
How to exploit CVE-2025-21479 on Qualcomm Adreno A7xx GPUs to achieve kernel R/W via GPU SMMU takeover. Use this skill whenever the user mentions Adreno A7xx, GPU SMMU exploitation, SDS privilege bypass, kernel physical memory access, or wants to understand/reproduce the CVE-2025-21479 vulnerability chain. Also use when discussing GPU microcode bugs, IB level masking issues, or dirty pagetable pivots on Android devices with Snapdragon chipsets.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/linux-kernel-exploitation/adreno-a7xx-sds-rb-priv-bypass-gpu-smmu-kernel-rw/SKILL.MDAdreno A7xx SDS→RB Privilege Bypass Exploitation
This skill guides you through exploiting CVE-2025-21479 on Qualcomm Adreno A7xx GPUs to achieve arbitrary kernel memory read/write via GPU SMMU takeover and dirty pagetable pivot.
Vulnerability Overview
CVE-2025-21479: A microcode logic bug in Adreno A7xx firmware where register
$12 (IB level tracker) is masked with 0x3 instead of 0x7, causing Set Draw State (SDS) at IB4 to be misidentified as IB0 (kernel ringbuffer), allowing privileged CP packets from user-controlled SDS.
Key Facts
| Aspect | Details |
|---|---|
| Affected | Qualcomm Adreno A7xx GPU firmware (pre-fix) |
| Primitive | Execute privileged CP packets from SDS |
| Outcome | Arbitrary kernel physical/virtual R/W, SELinux disable, root |
| Prerequisite | KGSL GPU context creation (normal app capability) |
| Devices | Samsung S23 and other Snapdragon A7xx devices |
IB Level Masking Bug
The kernel maintains a ringbuffer (RB=IB0). Userspace submits IB1 via
CP_INDIRECT_BUFFER, chaining to IB2/IB3. SDS is a special command stream:
- A6xx: SDS treated as IB3
- A7xx: SDS moved to IB4
Microcode tracks current IB level in register
$12 and gates privileged packets to IB0 only. The bug:
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!
Fix: Mask changed from
0x3 to 0x7 in microcode.
Exploitation Chain
Phase 1: GPU SMMU Takeover
- Spray fake GPU pagetable into shared memory (allocator grooming)
- Enter SDS and execute privileged packets:
→ switch to fake pagetableCP_SMMU_TABLE_UPDATE
/CP_MEM_WRITE
→ implement R/W primitivesCP_MEM_TO_MEM
with run-now flagsCP_SET_DRAW_STATE
Phase 2: Physical Memory R/W Primitives
Using the fake pagetable:
- Write:
to attacker-chosen GPU VA mapped to target PACP_MEM_WRITE - Read:
copies 4/8 bytes from target PA to userspace bufferCP_MEM_TO_MEM
Phase 3: Find Kernel Physical Base (Samsung KASLR)
Samsung randomizes kernel physical base within known region. Brute-force for
_stext signature:
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 ctx->kernel.pbase = base + offset - 0x10000; break; } }
Compute kernel virtual via linear map:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Phase 4: Dirty Pagetable Pivot (Fast CPU-side R/W)
GPU R/W is slow. Pivot to fast primitive by corrupting process PTEs:
- Locate
using GPU R/Wtask_struct → mm_struct → mm_struct->pgd
two adjacent userspace pages A and B (e.g., atmmap
)0x1000- Walk
to resolve A/B's PTE physical addressesPGD→PMD→PTE - Overwrite B's PTE to point to last-level pagetable managing A/B with RW attributes
- Write via B's VA to mutate A's PTE to map target PFNs
- Read/write kernel memory via A's VA, flushing TLB until sentinel flips
Implementation Helpers
Use the bundled scripts for common tasks:
- Build SDS command sequencesscripts/generate_sds_commands.py
- Brute-force kernel physical basescripts/find_kernel_physbase.py
- Template for dirty pagetable techniquescripts/dirty_pagetable_pivot.py
Detection & Mitigation
Detection
- Alert if
appears outside RB/IB0, especially in SDSCP_SMMU_TABLE_UPDATE - Monitor anomalous bursts of 4/8-byte
CP_MEM_TO_MEM - Watch for excessive TLB flush patterns
Mitigation
- Update GPU microcode to use
mask for register0x7$12 - Validate IB level before accepting privileged CP packets
- Monitor GPU command submission for SDS privilege escalation attempts
Important Notes
- Excessive GPU traffic can cause UI blackouts and reboots
- Reads are small (4/8 bytes) and sync is slow by default
- Each Android process gets a KGSL context (
)IOCTL_KGSL_GPU_CONTEXT_CREATE - Firmware variations exist - use freedreno's afuc/packet docs for exact encodings