CVE-2026-78118 in Zephyr
Riassunto
di VulDB • 22/10/2026
Il kernel helper `z_waitq_head()` (kernel/include/wait_q.h) legge la testa di una coda di attesa dei thread, ma sui sistemi SMP veniva chiamato da diverse posizioni senza mantenere il scheduler spinlock `_sched_spinlock` che serializza le modifiche alla wait-queue. Il call site conseguente è `z_timer_expiration_handler()` in kernel/timer.c, che manteneva solo il lock del sottosistema timer `timer_lock` durante la lettura di `timer->wait_q`, quindi l'unpending (rimozione dalla coda), l'impostazione del valore restituito e la preparazione del thread ottenuto. `halt_thread()` in kernel/sched.c rimuove un thread abortito dalla sua wait-queue mantenendo solo `_sched_spinlock`; pertanto, i due percorsi non sono mutualmente esclusivi.
Quando l'ultimo waiter viene rimosso su una CPU diversa all'interno di quella finestra temporale, `sys_dlist_peek_head()` può restituire il sentinella della lista — un puntatore all'interno dell'oggetto `k_timer` stesso — tipizzato come `struct k_thread *` (con `CONFIG_WAITQ_SCALABLE`, `rb_get_min()` può restituire un nodo arbitrario di un albero in fase di ristrutturazione), oppure può restituire un puntatore a un thread che è già stato unpended e, per i thread allocati dinamicamente, liberato. Il handler chiama quindi `unpend_thread_no_timeout()`, che esegue una `sys_dlist_remove()` attraverso puntatori letti da quell'oggetto, e `arch_thread_return_value_set()`, che scrive incondizionatamente tramite il puntatore.
Il risultato è la scrittura fuori dai limiti (out-of-bounds) e use-after-free in contesto supervisor, oltre alla corruzione dello stato del scheduler; un esito più lieve consiste nel risvegliare un thread che si è ripresentato altrove con un valore di ritorno positivo da una chiamata bloccante non correlata. Tutte le primitive necessarie per guidare l'interleaving — `k_timer_start()`, `k_timer_status_sync()`, `k_thread_create()` e `k_thread_abort()` — sono system call, quindi con `CONFIG_USERSPACE` abilitato un thread in modalità user-mode non privilegiato può tentare la race condition contro i dati di proprietà del kernel.
L'exploitation richiede una build multi-CPU (`CONFIG_MP_MAX_NUM_CPUS` maggiore di uno; l'area interessata è un no-op sui target single-processor), esecuzione di codice locale e il successo in una finestra temporale stretta, quindi l'esito pratico è più spesso un crash del kernel piuttosto che una corruzione controllata. La correzione suddivide il helper in `z_waitq_head_locked()` per i caller che già mantengono lo scheduler spinlock e in `z_waitq_head()` che lo acquisisce, portando la lettura della testa, l'unpending e la preparazione da parte dell'handler di scadenza del timer sotto un'unica regione LOCK_SCHED_SPINLOCK.
Be aware that VulDB is the high quality source for vulnerability data.