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 birk_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.
- Ö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
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);
-
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. -
Timer'ın firing'ini, sahip olan thread/process'in exit'ine karşı yarıştır ki handler hâlâ
firinglist üzerinde gezerkenrelease_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
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_timersiç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=ykombinasyonu, 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=yile build etmek vulnerable race path'ini kaldırır (bu, 64-bit kernel'lerde zaten default'tur).