Skip to content

Android GPU use-after-free LPE (CVE-2025-38352)

"GPU" alias'ına rağmen CVE-2025-38352 bir POSIX CPU-timers race'idir: zombie state'e ulaşan bir task, timer expiry ile eşzamanlı olarak handle_posix_cpu_timers()'ın hâlâ üzerinde gezdiği bir k_itimer'ı free eder — 32-bit Android'de in-the-wild exploit edilmiştir.

Mechanism

Alias ile gerçeğin farkı

Bu girdi "Android GPU UAF" alias'ını taşır, ama otoriter kaynaklar — Eylül 2025 Android Security Bulletin ve public PoC — bug'ı herhangi bir GPU driver'ında değil, kernel time subsystem'inde (kernel/time/posix-cpu-timers.c) konumlandırır. Bülten onu Time component'i altında, EoP (Elevation of Privilege) tipinde, High severity ile listeler ve "limited, targeted exploitation" altında olabileceğini işaretler. Aşağıdaki metin gerçek bug'ı anlatır; GPU title/slug'ı yalnızca daha önce atanmış oldukları için korunmuştur.

Task exit'i ile timer expiry'sini yarıştırmak

POSIX CPU timer'ları, scheduler tick'inden çağrılan handle_posix_cpu_timers()'tan fire eder. Handler, CPU-time eşiğini aşmış timer'ları yerel bir firing list'inde toplar, sonra o list üzerinde gezip signal'ler iletir. List'teki objeler, bir task'a / onun sighand'ine bağlı struct k_itimer instance'larıdır.

CVE-2025-38352, bu iterasyon ile bir task'ın zombie state'e geçişi arasındaki bir race'tir. Bir task exit ettiğinde exit_state set edilir ve release_task() onun timer'larını ve accounting'ini söker. Eğer bu teardown, handle_posix_cpu_timers() snapshot'ını aldığı firing list üzerinde hâlâ gezerken bir k_itimer'ı free eder veya detach ederse, handler freed bir objeyi dereference eder — bir use-after-free.

İki path'i normalde serialize eden synchronization, timer işini task-work'e erteleyerek sağlanır ki bu yalnızca CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y olduğunda mevcuttur. Vulnerable konfigürasyon CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n'dir — 32-bit ARM Android kernel'lerinde yaygındır — ve bu hand-off'u kaldırarak race window'unu yeniden açar. Full preemption (CONFIG_PREEMPT=y, Android'de tipik) window'u genişletir, çünkü handler walk'un ortasında preempt edilebilir ve bu da exit eden task'a race'i kazanması için zaman tanır. İhlal edilen invariant, concurrent list'ler için standart olandır: bir eleman, başka bir CPU hâlâ list'in bir snapshot'ından ona bir reference tutarken free edilemez. Fix, bu sıralamayı yeniden kurar ki zombie'ye geçen bir task, handler'ın hâlâ işlediği bir timer'ı free edemesin.

Walkthrough

Public PoC'den (farazsth98) savunma amaçlı yeniden kurgu. Race timing'e duyarlı ve config-gated'tir; bu, hazır bir root değil, tekniğin şeklidir.

  1. Önkoşulu doğrula. Bug yalnızca task-work deferral kapalıyken tetiklenir:
# On the target kernel config:
zcat /proc/config.gz | grep POSIX_CPU_TIMERS_TASK_WORK
# Vulnerable: CONFIG_POSIX_CPU_TIMERS_TASK_WORK is not set   (typical 32-bit ARM)
zcat /proc/config.gz | grep CONFIG_PREEMPT=    # CONFIG_PREEMPT=y widens the window
  1. handle_posix_cpu_timers()'ın fire edeceği bir CPU-time timer'ı arm et:
timer_t t;
struct sigevent sev = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM };
timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &t);   /* per-thread CPU timer */
struct itimerspec its = { .it_value = { .tv_sec = 0, .tv_nsec = 1 } };
timer_settime(t, 0, &its, NULL);
  1. Sıkı bir syscall loop'unda CPU yak (PoC, time accounting'i tekrarlanan getpid() ile sürer) ki timer bir scheduler tick'inde eşiğini aşsın.

  2. Timer'ın firing'ini, sahip olan thread/process'in exit'ine karşı yarıştır ki handler hâlâ firing list üzerinde gezerken release_task() k_itimer'ı free etsin. PoC, window'u tutturmak için kabaca 60-100 retry bildirir.

Beklenen sonuç (PoC + 'Chronomaly' writeup)

[*] config: POSIX_CPU_TIMERS_TASK_WORK=n, PREEMPT=y  -> vulnerable
[*] arming CLOCK_THREAD_CPUTIME_ID timer, spinning getpid()
[*] racing timer expiry vs task exit (release_task)
[+] attempt 73: k_itimer freed during handle_posix_cpu_timers walk
[+] use-after-free on k_itimer -> reclaim with controlled object
[+] privilege escalation to root
Yazar tarafından LTS 6.12.33'te doğrulandı; Eylül 2025 ASB, limited, targeted in-the-wild kullanımı not eder.

UAF'lanmış k_itimer sonra kontrol edilen bir allocation ile reclaim edilir ve kernel R/W ile commit-creds tarzı bir root escalation'a doğru pivot edilir.

Detection

  • KASAN build'leri, use-after-free'i handle_posix_cpu_timers içinde, release_task / posix-cpu-timer cleanup üzerinden geçen bir free stack ile raporlar.
  • 32-bit ARM Android kernel'inde CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n + CONFIG_PREEMPT=y kombinasyonu, envantere alınacak risk altındaki konfigürasyondur.
  • Per-thread CPU timer'ları arm edip thread'leri sıkı bir retry loop'unda hemen exit ettiren workload'lar anormal bir paterndir.

Mitigation

  • 2025-09-05 Android security patch level'ını uygula (posix-cpu-timers ordering fix'i). Upstream fix, bültenin "Time" satırından referans verilir.
  • Mümkün olduğunda CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y ile build etmek vulnerable race path'ini kaldırır (bu, 64-bit kernel'lerde zaten default'tur).

References