Instituto Nacional de ciberseguridad. Sección Incibe
Instituto Nacional de Ciberseguridad. Sección INCIBE-CERT

CVE-2026-64560

Gravedad:
Pendiente de análisis
Tipo:
No Disponible / Otro tipo
Fecha de publicación:
29/07/2026
Última modificación:
29/07/2026

Descripción

*** Pendiente de traducción *** In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> posix-cpu-timers: Prevent UAF caused by non-leader exec() race<br /> <br /> Wongi and Jungwoo decoded and reported a non-leader exec() related race<br /> which can result in an UAF:<br /> <br /> sys_timer_delete() exec()<br /> posix_cpu_timer_del()<br /> // Observes old leader<br /> p = pid_task(pid, pid_type); de_thread()<br /> switch_leader();<br /> release_task(old_leader)<br /> __exit_signal(old_leader)<br /> sighand = lock(old_leader, sighand);<br /> posix_cpu_timers*_exit();<br /> sighand = lock_task_sighand(p) unhash_task(old_leader);<br /> sh = lock(p, sighand) old_leader-&gt;sighand = NULL;<br /> unlock(sighand);<br /> (p-&gt;sighand == NULL)<br /> unlock(sh)<br /> return NULL;<br /> <br /> // Returns without action<br /> if(!sighand)<br /> return 0;<br /> free_posix_timer();<br /> <br /> This is "harmless" unless the deleted timer was armed and enqueued in<br /> p-&gt;signal because on exec() a TGID targeted timer is inherited.<br /> <br /> As sys_timer_delete() freed the underlying posix timer object<br /> run_posix_cpu_timers() or any timerqueue related add/delete operations on<br /> other timers will access the freed object&amp;#39;s timerqueue node, which results<br /> in an UAF.<br /> <br /> There is a similar problem vs. posix_cpu_timer_set(). For regular posix<br /> timers it just transiently returns -ESRCH to user space, but for the use<br /> case in do_cpu_nanosleep() it&amp;#39;s the same UAF just that the k_itimer is<br /> allocated on the stack.<br /> <br /> Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops<br /> to expire.<br /> <br /> While debating solutions Frederic pointed out another problem:<br /> <br /> posix_cpu_timer_del(tmr)<br /> __exit_signal(p)<br /> posix_cpu_timers*_exit(p);<br /> unhash_task(p);<br /> p-&gt;sighand = NULL;<br /> sh = lock_task_sighand(p)<br /> sighand = p-&gt;sighand;<br /> if (!sighand)<br /> return NULL;<br /> lock(sighand);<br /> <br /> if (!sh)<br /> WARN_ON_ONCE(timer_queued(tmr));<br /> <br /> On weakly ordered architectures it is not guaranteed that<br /> posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()<br /> when p-&gt;sighand is observed as NULL, which means the WARN() can be a false<br /> positive.<br /> <br /> Solve these issues by:<br /> <br /> 1) Changing the store in __exit_signal() to smp_store_release().<br /> <br /> 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path<br /> of lock_task_sighand().<br /> <br /> 3) Creating a helper function for looking up the task and locking sighand<br /> which does not return when sighand == NULL. Instead it retries the<br /> task lookup and only if that fails it gives up.<br /> <br /> 4) Using that helper in the three affected functions.<br /> <br /> #1/#2 ensures that the reader side which observes sighand == NULL also<br /> observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()<br /> and the ones in unhash_task().<br /> <br /> #3 ensures that the above described non-leader exec() situation is handled<br /> gracefully. When the task lookup returns the old leader, but sighand ==<br /> NULL then it retries. In the non-leader exec() case the subsequent task<br /> lookup will observe the new leader due to #1/#2. In normal exit() scenarios<br /> the subsequent lookup fails.<br /> <br /> When the task lookup fails, the function also checks whether the timer is<br /> still enqueued and issues a warning if that&amp;#39;s the case. Unfortunately there<br /> is nothing which can be done about it, but as the task is already not<br /> longer visible the timer should not be accessed anymore. This check also<br /> requires memory ordering, which is not provided when the first lookup<br /> fails. To achieve that the check is preceeded by a smp_rmb() which pairs<br /> with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that<br /> the stores in posix_cpu_timers*_exit() are visible.<br /> <br /> The history of the non-leader exec() issue goes back to the early days of<br /> posix CPU timers, which stored a pointer to the group leader task in the<br /> timer. That obviously fails when a non-leader exec() switches the leader.<br /> commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems<br /> with mt exec") added a temporary workaround for that in 2010 which surv<br /> ---truncated---

Impacto