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.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/binary-exploitation/linux-kernel-exploitation/adreno-a7xx-sds-rb-priv-bypass-gpu-smmu-kernel-rw/SKILL.MD
source content

Adreno 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

AspectDetails
AffectedQualcomm Adreno A7xx GPU firmware (pre-fix)
PrimitiveExecute privileged CP packets from SDS
OutcomeArbitrary kernel physical/virtual R/W, SELinux disable, root
PrerequisiteKGSL GPU context creation (normal app capability)
DevicesSamsung 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

  1. Spray fake GPU pagetable into shared memory (allocator grooming)
  2. Enter SDS and execute privileged packets:
    • CP_SMMU_TABLE_UPDATE
      → switch to fake pagetable
    • CP_MEM_WRITE
      /
      CP_MEM_TO_MEM
      → implement R/W primitives
    • CP_SET_DRAW_STATE
      with run-now flags

Phase 2: Physical Memory R/W Primitives

Using the fake pagetable:

  • Write:
    CP_MEM_WRITE
    to attacker-chosen GPU VA mapped to target PA
  • Read:
    CP_MEM_TO_MEM
    copies 4/8 bytes from target PA to userspace buffer

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:

  1. Locate
    task_struct → mm_struct → mm_struct->pgd
    using GPU R/W
  2. mmap
    two adjacent userspace pages A and B (e.g., at
    0x1000
    )
  3. Walk
    PGD→PMD→PTE
    to resolve A/B's PTE physical addresses
  4. Overwrite B's PTE to point to last-level pagetable managing A/B with RW attributes
  5. Write via B's VA to mutate A's PTE to map target PFNs
  6. Read/write kernel memory via A's VA, flushing TLB until sentinel flips

Implementation Helpers

Use the bundled scripts for common tasks:

  • scripts/generate_sds_commands.py
    - Build SDS command sequences
  • scripts/find_kernel_physbase.py
    - Brute-force kernel physical base
  • scripts/dirty_pagetable_pivot.py
    - Template for dirty pagetable technique

Detection & Mitigation

Detection

  • Alert if
    CP_SMMU_TABLE_UPDATE
    appears outside RB/IB0, especially in SDS
  • Monitor anomalous bursts of 4/8-byte
    CP_MEM_TO_MEM
  • Watch for excessive TLB flush patterns

Mitigation

  • Update GPU microcode to use
    0x7
    mask for register
    $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

References