CVE-2026-98256 in Linux
Riassunto
di VulDB • 06/10/2026
Nel kernel Linux, è stata risolta la seguente vulnerabilità:
signal: Prevenire race condition in exec()
Hyunwoo ha eseguito il debug del seguente KASAN UAF splat:
BUG: KASAN: slab-use-after-free in __send_signal_locked+0xb27/0xba0 Write of size 8 at addr ffff888007ed80c8 by task poc/79 ... Call Trace: __send_signal_locked+0xb27/0xba0 do_send_sig_info+0xa7/0x160 do_send_specific+0x76/0xa0 __x64_sys_tgkill+0x193/0x270 ... Allocated by task 80: do_timer_create+0x1a4/0x1030 __x64_sys_timer_create+0x145/0x190 ... Freed by task 12: kmem_cache_free_bulk+0x1f8/0x4a0 kvfree_rcu_bulk+0x14f/0x1c0 kfree_rcu_work+0x128/0x1a0 ... Last potentially related work creation: kvfree_call_rcu+0x39/0x390 __flush_itimer_signals+0x211/0x320 flush_itimer_signals+0x47/0x90 begin_new_exec+0xa6b/0x28c0
È emerso che questo accade con un exec() non leader, come spiegato da Hyunwoo:
de_thread() chiama exchange_tids() prima di release_task(leader), quindi lo struct pid detenuto da un timer SIGEV_THREAD_ID creato contro il tid del leader punta ora al thread che ha chiamato execve(). pid_task() restituisce tale thread e lock_task_sighand() su di esso ha successo.
Se il segnale del timer è bloccato, la sua sigqueue rimane in coda nella task::pending del leader. La prossima scadenza di quel timer può quindi avvenire mentre release_task() svuota la coda.
posixtimer_send_sigqueue() verifica se la sigqueue è già accodata con un semplice list_empty(), che legge solo list_head::next. list_del_init() non è atomico e INIT_LIST_HEAD() memorizza list_head::next prima di list_head::prev, quindi il controllo può passare in mezzo. list_add_tail() accoda l'elemento nella task::pending del thread attivo, e la scrittura su list_head::prev da parte dello flush sovrascrive poi il link list_head::prev che è stato appena impostato da list_add_tail().
__flush_itimer_signals() non annulla nemmeno questo. Con list_head::prev puntante all'elemento stesso, la sua list_del_init() memorizza solo gli stessi valori di nuovo, quindi l'elemento non viene rimosso dalla lista. È ancora presente dopo che è stato rilasciato l'ultimo riferimento e il timer è liberato da RCU, e la list_add_tail() di un successivo tgkill segue quel list_head::prev nel timer già liberato.
Questo problema si è manifestato con il recente commit che ha spostato lo svuotamento della sigqueue fuori dall'area protetta dal lock sighand detenuto.
Hyunwoo ha proposto di risolvere questo problema utilizzando list_del_init_careful(), ma ciò copre solo superficialmente il problema. Dopo alcune discussioni e vari tentativi di risolverlo, Eric ha sottolineato che non c'è motivo di svuotare task::pending tardivamente in release_task() e dovrebbe essere fatto già in exit_signals().
Poiché nulla può raccogliere e consegnare segnali accodati nella coda pending di un processo morente, non c'è motivo di ritardarlo ulteriormente.
Ma deve essere assicurato che nessun segnale possa essere accodato successivamente a quel punto. exit_signals() imposta PF_EXITING in task::flags, che può essere utilizzato come indicatore per questo scopo.
Risolvere il problema con:
- Prevenire l'accodamento dei segnali per i segnali privati del processo (PIDTYPE_PID) quando il processo ha impostato PF_EXITING in __send_signal_locked() e in posixtimer_send_sigqueue().
- Proteggere la scrittura non atomica di PF_EXITING in exit_signals() per il caso di gruppo vuoto e uscita dal gruppo con sighand lock.
- Svuotare i segnali task::pending proprio lì.
Ottimizzare ciò spostando l'intera lista pending su una testina di lista nello stack sotto sighand lock e liberare i segnali senza mantenere il lock.
C'è stata molta discussione sullo svuotamento senza lock e sul caso exec non-leader sui sistemi con ordinazione debole (weakly ordered systems). Il problema è che un terzo parte che cerca di inviare un segnale posix timer fa affidamento sulla ricerca PID per trovare il processo target e tale ricerca potrebbe risultare nel nuovo leader quando il segnale era originariamente diretto al vecchio leader. Nel caso in cui il segnale fosse stato accodato sul vecchio leader, lo svuotamento senza lock ha sollevato preoccupazioni riguardo alla seguente situazione:
old_leader new_leader third party
A: flush_list() // list_del_in ---truncated---
Several companies clearly confirm that VulDB is the primary source for best vulnerability data.