
Análisis de UAF en el kernel de Linux para CVE-2026-64560 con PoC de activación por condición de carrera, revisión del parche, matriz de versiones LTS/Android afectadas y autocomprobación para dispositivos parcheados.
Reproductor / PoC (de verificación de disparo): Linux y Android (NDK) Este repositorio es únicamente para verificar el estado del parche y para estudio e investigación en dispositivos de prueba propios; no incluye ninguna primitiva de escalada de privilegios/explotación.
| Campo | Contenido |
|---|---|
| CVE ID | CVE-2026-64560 |
| Título | posix-cpu-timers: Prevent UAF caused by non-leader exec() race |
| Tipo | Use-After-Free (CWE-416), condición de carrera |
| CNA | kernel.org (Linux CNA) |
| CVSS v3.1 | 7.8 Alta — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CVSS v4.0 (SUSE) | 8.5 Alta — CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N |
| EPSS | ~0.12 % (percentil 2, a fecha de 2026-08) |
| CISA KEV | No incluida |
| Fecha de divulgación | 2026-07-29 |
| Commit de corrección (mainline) | 920f893f735e92ba3a1cd9256899a186b161928d |
| Commit que introdujo el problema (Fixes:) | 55e8c8eb2c7b (v5.7, 2020) — "posix-cpu-timers: Store a reference to a pid not a task" |
| Corrección por | Thomas Gleixner <[email protected]> |
| Informado por | Wongi Lee <[email protected]>, Jungwoo Lee <[email protected]> |
| Archivos afectados | kernel/exit.c, kernel/signal.c, kernel/time/posix-cpu-timers.c |
La vulnerabilidad se introdujo en v5.7 (2020-05) y la corrección se ha backporteado a las distintas ramas estables:
| Rama | Afectadas | Versión corregida (≥) | Commit estable de corrección |
|---|---|---|---|
| 5.10 LTS | 5.7 ~ 5.10.261 | 5.10.262 | 67aa823e3e8c |
| 5.15 LTS | ~ 5.15.212 | 5.15.213 | d8bcb28abad8 |
| 6.1 LTS | ~ 6.1.179 | 6.1.180 | cc35ddbc4973 |
| 6.6 LTS | ~ 6.6.146 | 6.6.147 | 12a891c773ae |
| 6.12 LTS | ~ 6.12.99 | 6.12.100 | e74443f5db00 |
| 6.18 | ~ 6.18.40 | 6.18.41 | 6a7ecc25abe6 |
| 7.1 | ~ 7.1.4 | 7.1.5 | ad1cafa1bdaa |
| mainline | < 7.2-rc3 | 7.2-rc3 | 920f893f735e |
Los kernels GKI de Android se basan en las LTS 5.10 / 5.15 / 6.1 / 6.6 / 6.12, todas dentro del rango afectado. Dado que el commit de corrección en mainline se publicó el 2026-07-29, los dispositivos Android con SPL (nivel de parche de seguridad) 2026-08-01 o anterior prácticamente no incluyen esta corrección. Puede comprobar la versión del kernel y el SPL en el dispositivo con adb shell cat /proc/version y getprop ro.build.version.security_patch.
Los temporizadores de CPU POSIX (timer_create(CLOCK_PROCESS_CPUTIME_ID, ...) / timer_create(CLOCK_THREAD_CPUTIME_ID, ...)) son gestionados en el kernel por kernel/time/posix-cpu-timers.c. Cada k_itimer recuerda la tarea objetivo mediante it.cpu.pid; al operar sobre el temporizador se necesita lock_task_sighand(p, &flags) para obtener el sighand->siglock de dicha tarea y proteger la timerqueue.
El commit de 2020 55e8c8eb2c7b sustituyó el puntero a task que se cacheaba en el temporizador por una referencia pid (para corregir el problema introducido por el workaround de 2010 e0a70217107e), realizando una búsqueda pid_task(pid, type) antes de cada operación. Este cambio dejó abierta la ventana de carrera de este CVE.
Cuando execve() lo inicia un hilo no líder, de_thread() → switch_leader() transfiere el TGID del líder anterior al nuevo líder; el líder anterior pasa por release_task() → __exit_signal(), donde se establece old_leader->sighand = NULL y se ejecuta unhash_task(old_leader).
Mientras tanto, sys_timer_delete() → posix_cpu_timer_del() ejecutándose en otra CPU:
sys_timer_delete() exec()
posix_cpu_timer_del()
// 观察到旧 leader
p = pid_task(pid, pid_type); de_thread()
switch_leader();
release_task(old_leader)
__exit_signal(old_leader)
sighand = lock(old_leader, sighand);
posix_cpu_timers*_exit();
sighand = lock_task_sighand(p) unhash_task(old_leader);
sh = lock(p, sighand) old_leader->sighand = NULL;
unlock(sighand);
(p->sighand == NULL)
unlock(sh)
return NULL;
// 直接返回,没有摘链!
if (!sighand)
return 0;
free_posix_timer(); // ← k_itimer 被释放
El p que obtiene posix_cpu_timer_del() es el líder anterior; en ese momento p->sighand == NULL, por lo que la función considera que «la tarea está saliendo y la ruta de exit se encargará de desencolar», así que no hace nada y devuelve éxito. A continuación, free_posix_timer() libera el k_itimer.
Punto clave: a diferencia de exit(), en exec() el TGID no cambia; los temporizadores armados colgados a nivel de proceso (p->signal->cpu_timers) se heredan y permanecen encolados. Por lo tanto:
run_posix_cpu_timers() (recorre la timerqueue en el tick) accede al timerqueue_node del objeto ya liberado → UAF de lectura/escritura;Problemas similares también existen en:
posix_cpu_timer_set(): para un temporizador normal solo devuelve temporalmente -ESRCH; pero el do_cpu_nanosleep() interno del kernel usa un k_itimer asignado en la pila, por lo que es el mismo UAF.posix_cpu_timer_rearm(): el rearranque falla silenciosamente y el temporizador ya no vence (bug funcional).Frederic Weisbecker señaló que tsk->sighand = NULL en __exit_signal() es un almacenamiento ordinario; en arquitecturas con ordenación débil como ARM64, cuando posix_cpu_timer_del() observa sighand == NULL, no se garantiza que observe la escritura de desencolado anterior a posix_cpu_timers*_exit(), lo que puede provocar un falso positivo en WARN_ON_ONCE(timer_queued(tmr)).
__exit_signal() se cambia a smp_store_release(&tsk->sighand, NULL);!sighand de lock_task_sighand() se añade smp_acquire__after_ctrl_dep();timer_lock_sighand(): busca la task y bloquea el sighand; si sighand == NULL, no retorna sino que reintenta la búsqueda: en el escenario exec encontrará al nuevo líder; en el escenario exit solo se abandona si la búsqueda falla;_del / _set / _rearm) pasan a usar este helper de forma unificada.El diff completo está en patches/920f893f735e.patch.
En poc/ se proporciona un disparador de carrera: dos hilos en bucles de alta intensidad, respectivamente: