POSIX CPU Timers TOCTOU race (CVE-2025-38352)
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 स्थिति का दस्तावेजीकरण करता है जो टाइमर की स्थिति को भ्रष्ट कर सकती है और kernel को crash कर सकती है, और कुछ परिस्थितियों में इसे privilege escalation की ओर मोड़ा जा सकता है।
- प्रभावित घटक: kernel/time/posix-cpu-timers.c
- प्रिमिटिव: expiry vs deletion race under task exit
- कॉन्फ़िग संवेदनशील: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
Quick internals recap (relevant for exploitation)
- तीन CPU clocks timers के अकाउंटिंग को cpu_clock_sample() के माध्यम से नियंत्रित करते हैं:
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer creation एक 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 में प्रविष्टियाँ डालता है और संभवतः 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;
}
- तेज़ पथ महंगी प्रोसेसिंग तब तक टालता है जब तक कैश्ड एक्सपायरीज़ संभावित फायरिंग का संकेत न दें:
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 समाप्त हुए टाइमरों को इकट्ठा करता है, उन्हें firing के रूप में चिह्नित करता है, और उन्हें queue से हटा देता है; वास्तविक delivery बाद के लिए स्थगित कर दी जाती है:
#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 संदर्भ में संभाली जाती है
POSIX CPU timer के चलने के रास्ते
```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 path में, firing list को sighand के बाहर प्रोसेस किया जाता है
IRQ-context handling path
```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); } } ```मूल कारण: TOCTOU - IRQ-समय समाप्ति और task exit के दौरान समवर्ती हटाने के बीच TOCTOU Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK निष्क्रिय है (IRQ path in use)
- लक्षित task exit कर रहा है लेकिन पूरी तरह से reaped नहीं हुआ है
- एक अन्य थ्रेड समान timer के लिए posix_cpu_timer_del() को समवर्ती रूप से कॉल करता है
Sequence
- update_process_times() exiting task के लिए IRQ context में run_posix_cpu_timers() को trigger करता है।
- collect_timerqueue() ctmr->firing = 1 सेट करता है और timer को temporary firing list में स्थानांतरित करता है।
- handle_posix_cpu_timers() lock के बाहर timers deliver करने के लिए unlock_task_sighand() के माध्यम से sighand छोड़ देता है।
- unlock के तुरंत बाद, exiting task को reaped किया जा सकता है; एक सह-थ्रेड posix_cpu_timer_del() चलाता है।
- इस विंडो में, posix_cpu_timer_del() cpu_timer_task_rcu()/lock_task_sighand() के माध्यम से state प्राप्त करने में विफल हो सकता है और इसलिए सामान्य in-flight guard जिसे timer->it.cpu.firing जांचता है, वह स्किप हो जाता है। Deletion ऐसे मानकर आगे बढ़ता है जैसे firing नहीं हो रही हो, जिससे expiry हैंडल होते समय state भ्रष्ट हो जाता है और crashes/UB होते हैं।
Why TASK_WORK mode is safe by design
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y होने पर, expiry को task_work पर टाला जाता है; exit_task_work exit_notify से पहले चलता है, इसलिए IRQ-time का reaping के साथ overlap नहीं होता।
- फिर भी, अगर task पहले से exit कर रहा है, तो task_work_add() विफल होता है; exit_state पर gating दोनों मोड्स को consistent बनाता है।
Fix (Android common kernel) and rationale
- Add an early return if current task is exiting, gating all processing:
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
- यह exit हो रहे tasks के लिए handle_posix_cpu_timers() में प्रवेश रोकता है, और उस विंडो को समाप्त कर देता है जहाँ posix_cpu_timer_del() it.cpu.firing को मिस कर सकता था और expiry processing के साथ रेस कर सकता था।
Impact
- timer structures के concurrent expiry/deletion के दौरान Kernel memory corruption तुरंत क्रैश (DoS) पैदा कर सकता है और arbitrary kernel-state manipulation के अवसरों के कारण privilege escalation की ओर जाने वाला एक मजबूत primitive है।
Triggering the bug (safe, reproducible conditions) Build/config
- सुनिश्चित करें CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n है और exit_state gating fix के बिना kernel का उपयोग करें।
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 से, समान timer को एक साथ हटाएँ जबकि target thread exit कर रहा हो:
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
- रेस बढ़ाने वाले घटक: उच्च scheduler tick rate, CPU load, बार-बार thread exit/re-create चक्र। क्रैश आमतौर पर तब प्रकट होता है जब posix_cpu_timer_del() firing को नोटिस करना छोड़ देता है क्योंकि unlock_task_sighand() के तुरंत बाद task lookup/locking विफल हो रहा होता है।
डिटेक्शन और हार्डनिंग
- निवारण: exit_state guard लागू करें; जहाँ संभव हो CONFIG_POSIX_CPU_TIMERS_TASK_WORK सक्षम करना प्राथमिकता दें।
- निगरानी: unlock_task_sighand()/posix_cpu_timer_del() के चारों ओर tracepoints/WARN_ONCE जोड़ें; जब it.cpu.firing==1 देखा जाए साथ ही cpu_timer_task_rcu()/lock_task_sighand() विफल हों तो अलर्ट करें; task exit के आसपास timerqueue असंगतियों पर नजर रखें।
ऑडिट हॉटस्पॉट्स (रीव्यूअर्स के लिए)
- update_process_times() → run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() selection (TASK_WORK vs IRQ path)
- collect_timerqueue(): ctmr->firing सेट करता है और नोड्स को मूव करता है
- handle_posix_cpu_timers(): firing loop से पहले sighand छोड़ता है
- posix_cpu_timer_del(): in-flight expiry का पता लगाने के लिए it.cpu.firing पर निर्भर करता है; यह जाँच तब स्किप हो जाती है जब task lookup/lock exit/reap के दौरान विफल होता है
एक्सप्लॉइटेशन रिसर्च के नोट्स
- प्रकटीकृत व्यवहार एक विश्वसनीय kernel crash primitive है; इसे privilege escalation में बदलने के लिए सामान्यतः एक अतिरिक्त controllable overlap (object lifetime या write-what-where प्रभाव) चाहिए होता है जो इस सारांश के दायरे से बाहर है। किसी भी PoC को संभावित रूप से अस्थिर करने वाला माना जाए और केवल emulators/VMs में ही चलाएँ।
Chronomaly exploit strategy (priv-esc without fixed text offsets)
- Tested target & configs: x86_64 v5.10.157 under QEMU (4 cores, 3 GB RAM). Critical options:
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n,CONFIG_PREEMPT=y,CONFIG_SLAB_MERGE_DEFAULT=n,DEBUG_LIST=n,BUG_ON_DATA_CORRUPTION=n,LIST_HARDENED=n. - Race steering with CPU timers: एक racing thread (
race_func()) CPU जलाता है जबकि CPU timers फ़ायर करते हैं;free_func()SIGUSR1पोल करता है यह पुष्टि करने के लिए कि timer फ़ायर हुआ।CPU_USAGE_THRESHOLDको इस तरह ट्यून करें कि सिग्नल कभी-कभी ही पहुँचें (इंटरमिटेंट “Parent raced too late/too early” मैसेज)। अगर timers हर प्रयास पर फ़ायर हों तो threshold घटाएँ; अगर थ्रेड exit से पहले वे कभी फ़ायर न हों तो threshold बढ़ाएँ। - Dual-process alignment into
send_sigqueue(): Parent/child प्रक्रियाएँsend_sigqueue()के अंदर दूसरे race विंडो को लक्षित करने की कोशिश करती हैं। parent timers आर्म करने से पहलेPARENT_SETTIME_DELAY_USमाइक्रोसेकंड सोता है; जब आप अधिकतर “Parent raced too late” देखते हैं तो इसे घटाएँ और जब अधिकतर “Parent raced too early” देखें तो बढ़ाएँ। दोनों दिखना संकेत है कि आप विंडो के ऊपर आ रहे हैं; एक बार ट्यून होने पर ~1 मिनट के भीतर सफलता अपेक्षित है। - Cross-cache UAF replacement: exploit एक
struct sigqueueफ्री करता है और फिर allocator स्थिति (sigqueue_crosscache_preallocs()) को गूम करके यह सुनिश्चित करता है कि danglinguaf_sigqueueऔर replacementrealloc_sigqueueदोनों pipe buffer data page पर आ जाएँ (cross-cache reallocation)। विश्वसनीयता मानती है कि kernel शांत हो और पहले से बहुत कमsigqueueallocations हों; यदि per-CPU/per-node partial slab पेज पहले से मौजूद हैं (व्यस्त सिस्टम), तो replacement चूक जाएगा और चेन फेल हो जाएगी। लेखक ने इसे noisy kernels के लिए जानबूझकर अनऑप्टिमाइज़्ड छोड़ा है।
इन्हें भी देखें
Ksmbd Streams Xattr Oob Write Cve 2025 37947
संदर्भ
- Race Against Time in the Kernel’s Clockwork (StreyPaws)
- Android security bulletin – September 2025
- Android common kernel patch commit 157f357d50b5…
- Chronomaly exploit PoC (CVE-2025-38352)
- CVE-2025-38352 analysis – Part 1
- CVE-2025-38352 analysis – Part 2
- CVE-2025-38352 analysis – Part 3
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 सबमिट करें।


