CVE-2026-98256 in Linuxinfo

Zusammenfassung

von VulDB • 06.10.2026

Im Linux-Kernel wurde folgende Schwachstelle behoben:

signal: Race-Bedingung bei exec() verhindern

Hyunwoo hat den folgenden KASAN UAF-Fehler (Use-After-Free) debuggt:

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

Es stellte sich heraus, dass dies bei einer exec()-Operation ohne Leader auftritt, wie Hyunwoo erklärte:

de_thread() ruft exchange_tids() vor release_task(leader) auf, sodass die struct pid, die von einem SIGEV_THREAD_ID-Timer gehalten wird, der gegen die tid des Leaders erstellt wurde, nun auf den Thread zeigt, der execve() aufgerufen hat. pid_task() gibt diesen Thread zurück und lock_task_sighhand() darauf erfolgreich.

Wenn das Timer-Signal blockiert ist, bleibt dessen sigqueue in task::pending des Leaders wartend (queued). Die nächste Ablaufzeit dieses Timers kann dann ausgeführt werden, während release_task() die Warteschlange leert.

posixtimer_send_sigqueue() prüft, ob sich die sigqueue bereits in der Warteschlange befindet, indem es eine einfache list_empty()-Prüfung durchführt, die nur list_head::next liest. list_del_init() ist nicht atomar und INIT_LIST_HEAD() speichert list_head::next vor list_head::prev, sodass die Prüfung dazwischen erfolgreich sein kann. list_add_tail() stellt den Eintrag in task::pending des lebenden Threads ein, und der Speichervorgang von list_head::prev durch das Leeren überschreibt dann den list_head::prev-Link, den list_add_tail() gerade gesetzt hat.

__flush_itimer_signals() macht dies auch nicht rückgängig. Da list_head::prev auf den Eintrag selbst zeigt, speichert dessen list_del_init() nur dieselben Werte erneut, sodass der Eintrag nicht aus der Liste entfernt wird. Er ist immer noch vorhanden, nachdem die letzte Referenz freigegeben wurde und der Timer durch RCU freigegeben wurde, und das list_add_tail() eines späteren tgkill() folgt diesem list_head::prev in den freigegebenen Timer hinein.

Dieses Problem trat mit dem jüngsten Commit auf, der das Leeren von sigqueue aus dem Bereich des gehaltenen sighand-Locks verschoben hat.

Hyonwoo schlug vor, dies durch die Verwendung von list_del_init_careful() zu beheben, aber das kaschiert das Problem nur. Nach einigen Diskussionen und verschiedenen Versuchen, es zu lösen, wies Eric darauf hin, dass kein Grund besteht, task::pending spät in release_task() zu leeren; dies sollte bereits in exit_signals() erfolgen.

Da nichts Signale sammeln und ausliefern kann, die in der pending-Warteschlange eines sterbenden Tasks gespeichert sind, gibt es keinen Grund, dies weiter hinauszuzögern.

Es muss jedoch sichergestellt werden, dass nach diesem Punkt keine weiteren Signale dort eingereiht werden können. exit_signals() setzt PF_EXITING in task::flags, was als Indikator dafür verwendet werden kann.

Behebung durch:

- Verhindern des Einreihens von Signalen für aufgabenbezogene Signale (PIDTYPE_PID), wenn der Task PF_EXITING in __send_signal_locked() und in posixtimer_send_sigqueue() gesetzt hat. - Schutz des nicht-lock-geschützten Setzens von PF_EXITING in exit_signals() für den Fall einer leeren Task-Gruppe bzw. eines Gruppen-Ausgangs mit dem sighand-Lock - Leeren der task::pending-Signale direkt dort.

Optimierung durch Verschieben der gesamten pending-Liste auf einen on-stack list head unter sighand-Lock und Freigabe der Signale ohne gehaltenen Lock.

Es gab erhebliche Diskussionen über das locklose Leeren und den Fall von exec() ohne Leader auf schwach geordneten Systemen (weakly ordered systems). Das Problem besteht darin, dass eine Drittpartei, die versucht, ein posix-Timersignal zu senden, sich für die Suche des Ziel-Tasks auf die PID-Suche verlässt; diese Suche kann zum neuen Leader führen, wenn das Signal ursprünglich an den alten Leader gerichtet war. Falls das Signal am alten Leader eingereiht wurde, dann hat das locklose Leisen eine Bedenken hinsichtlich der folgenden Situation ausgelöst:

old_leader new_leader third party

A: flush_list() // list_del_in ---truncated---

If you want to get best quality of vulnerability data, you may have to visit VulDB.

Zuständig

Linux

Reservieren

25.09.2026

Veröffentlichung

06.10.2026

Moderieren

akzeptiert

Eintrag

VDB-414050

EPSS

0.00000

KEV

nein

Aktivitäten

very low

Quellen

Want to know what is going to be exploited?

We predict KEV entries!