
Analisi UAF del kernel Linux per CVE-2026-64560 con PoC che innesca una race condition, revisione della patch, matrice delle versioni LTS/Android interessate e autoverifica per i dispositivi patchati.
Reproducer / PoC (tipo trigger di verifica): Linux e Android (NDK) Questo repository serve esclusivamente per verificare lo stato delle patch su dispositivi di test di propria proprietà e per ricerca/studio; non contiene alcuna primitiva di privilege escalation o exploit.
| Campo | Contenuto |
|---|---|
| CVE ID | CVE-2026-64560 |
| Titolo | "posix-cpu-timers: Prevent UAF caused by non-leader exec() race" |
| Tipo | Use-After-Free (CWE-416), condizione di gara (race) |
| CNA | kernel.org (Linux CNA) |
| CVSS v3.1 | 7.8 High — 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 High — 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% (2° percentile, al 2026-08) |
| CISA KEV | Non incluso |
| Data di divulgazione | 2026-07-29 |
| Commit di fix (mainline) | 920f893f735e92ba3a1cd9256899a186b161928d |
| Commit che ha introdotto il problema (Fixes:) | 55e8c8eb2c7b (v5.7, 2020) — "posix-cpu-timers: Store a reference to a pid not a task" |
| Autore della fix | Thomas Gleixner <[email protected]> |
| Segnalatori | Wongi Lee <[email protected]>, Jungwoo Lee <[email protected]> |
| File interessati | kernel/exit.c, kernel/signal.c, kernel/time/posix-cpu-timers.c |
La vulnerabilità è stata introdotta in v5.7 (2020-05) e il fix è stato backportato ai vari rami stable:
| Ramo | Vulnerabile | Versione fix (≥) | Commit di fix Stable |
|---|---|---|---|
| 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 |
I kernel GKI di Android si basano sugli LTS 5.10 / 5.15 / 6.1 / 6.6 / 6.12, tutti nell'intervallo interessato. Poiché il commit di fix mainline è stato pubblicato il 2026-07-29, i dispositivi Android con SPL (Security Patch Level) del 2026-08-01 o precedente in pratica non includono il fix. Sul dispositivo è possibile verificare la versione del kernel e l'SPL con adb shell cat /proc/version e getprop ro.build.version.security_patch.
I timer CPU POSIX (timer_create(CLOCK_PROCESS_CPUTIME_ID, ...) / timer_create(CLOCK_THREAD_CPUTIME_ID, ...)) sono gestiti nel kernel da kernel/time/posix-cpu-timers.c. Ogni k_itimer ricorda il task di destinazione tramite it.cpu.pid; per operare sul timer è necessario acquisire sighand->siglock del task tramite lock_task_sighand(p, &flags) per proteggere la timerqueue.
Il commit del 2020 55e8c8eb2c7b ha sostituito il puntatore a task memorizzato nel timer con un riferimento al pid (per correggere un problema introdotto dal workaround e0a70217107e del 2010), eseguendo una ricerca pid_task(pid, type) prima di ogni operazione. Questa modifica ha lasciato la finestra di race sfruttata da questa CVE.
Quando execve() viene invocato da un thread non leader, de_thread() → switch_leader() fa passare il TGID dal vecchio leader al nuovo leader; il vecchio leader passa attraverso release_task() → __exit_signal(), che imposta old_leader->sighand = NULL e chiama unhash_task(old_leader).
Nel frattempo, su un'altra CPU, sys_timer_delete() → posix_cpu_timer_del():
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 被释放
Il p trovato da posix_cpu_timer_del() è il vecchio leader; a questo punto p->sighand == NULL, quindi la funzione ritiene che "il task stia uscendo e che la path di exit rimuoverà il timer dalla coda" e quindi non fa nulla e restituisce successo. Subito dopo, free_posix_timer() libera il k_itimer.
Punto chiave: exec() è diverso da exit(): durante exec() il TGID rimane invariato, quindi i timer armati a livello di processo (p->signal->cpu_timers) vengono ereditati e restano in coda. Quindi:
run_posix_cpu_timers() (che scorre la timerqueue nel tick) accede al timerqueue_node di un oggetto già liberato → UAF in lettura/scrittura;Problemi analoghi esistono anche in:
posix_cpu_timer_set(): per i timer normali restituisce semplicemente -ESRCH; ma do_cpu_nanosleep() interno al kernel usa un k_itimer allocato sullo stack, con lo stesso UAF.posix_cpu_timer_rearm(): il rearm fallisce silenziosamente e il timer non scade più (bug funzionale).Frederic Weisbecker ha osservato che tsk->sighand = NULL in __exit_signal() è un semplice store; su architetture con memoria debolmente ordinata come ARM64, quando posix_cpu_timer_del() osserva sighand == NULL, non è garantito che osservi anche la scrittura di rimozione dalla coda eseguita prima di posix_cpu_timers*_exit(), quindi WARN_ON_ONCE(timer_queued(tmr)) può generare falsi positivi.
__exit_signal() è stato cambiato in smp_store_release(&tsk->sighand, NULL);!sighand di lock_task_sighand() è stata aggiunta smp_acquire__after_ctrl_dep();timer_lock_sighand(): cerca il task e blocca sighand; se sighand == NULL non restituisce, ma ritenta la ricerca — nello scenario exec trova il nuovo leader, mentre nello scenario exit abbandona solo se la ricerca fallisce;_del / _set / _rearm) ora usano tutte questo helper.Il diff completo è disponibile in patches/920f893f735e.patch.
La directory poc/ contiene un trigger di race: due thread eseguono cicli ad alta intensità: