
Analyse UAF du kernel Linux pour CVE-2026-64560 avec PoC déclenchant la condition de course, revue du correctif, matrice des versions LTS/Android concernées et auto-vérification pour les appareils corrigés.
Reproducteur / PoC (validation de déclenchement) : Linux & Android (NDK) Ce dépôt sert uniquement à vérifier l'état des correctifs et à des fins d'étude sur vos propres appareils de test ; il ne contient aucun primitif d'élévation de privilèges ni d'exploitation.
| Champ | Contenu |
|---|---|
| CVE ID | CVE-2026-64560 |
| Titre | posix-cpu-timers: Prevent UAF caused by non-leader exec() race |
| Type | Use-After-Free (CWE-416), condition de course |
| 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 % (2e percentile, août 2026) |
| CISA KEV | Non répertorié |
| Date de divulgation | 2026-07-29 |
| Commit du correctif (mainline) | 920f893f735e92ba3a1cd9256899a186b161928d |
| Commit à l'origine du problème (Fixes:) | 55e8c8eb2c7b (v5.7, 2020) — "posix-cpu-timers: Store a reference to a pid not a task" |
| Auteur du correctif | Thomas Gleixner <[email protected]> |
| Rapporteur(s) | Wongi Lee <[email protected]>, Jungwoo Lee <[email protected]> |
| Fichiers concernés | kernel/exit.c, kernel/signal.c, kernel/time/posix-cpu-timers.c |
La vulnérabilité a été introduite dans v5.7 (2020-05) ; le correctif a été backporté vers les différentes branches stables :
Les noyaux Android GKI sont basés sur les LTS 5.10 / 5.15 / 6.1 / 6.6 / 6.12, tous compris dans le périmètre affecté. Le commit de correctif mainline ayant été publié le 2026-07-29, les appareils Android avec un SPL (niveau de correctif de sécurité) daté du 2026-08-01 ou antérieur n'ont, pour la plupart, pas intégré ce correctif. Vous pouvez confirmer la version du noyau et le SPL sur l'appareil avec adb shell cat /proc/version et getprop ro.build.version.security_patch.
Les minuteurs CPU POSIX (timer_create(CLOCK_PROCESS_CPUTIME_ID, ...) / timer_create(CLOCK_THREAD_CPUTIME_ID, ...)) sont gérés dans le noyau par kernel/time/posix-cpu-timers.c. Chaque k_itimer mémorise la tâche cible via it.cpu.pid ; pour manipuler un minuteur, il faut obtenir le sighand->siglock de la tâche via lock_task_sighand(p, &flags) afin de protéger la timerqueue.
Le commit de 2020 55e8c8eb2c7b a remplacé le pointeur de tâche mis en cache dans le minuteur par une référence de pid (pour corriger le problème introduit par le workaround de 2010 e0a70217107e), avec une recherche pid_task(pid, type) avant chaque opération. Cette modification a laissé la fenêtre de course à l'origine de cette CVE.
Lorsque execve() est initié par un thread non-leader, de_thread() → switch_leader() fait passer le TGID de l'ancien leader au nouveau leader ; l'ancien leader passe par release_task() → __exit_signal(), qui y met old_leader->sighand = NULL et exécute unhash_task(old_leader).
Pendant ce temps, sys_timer_delete() → posix_cpu_timer_del() s'exécute sur un autre 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 被释放
Le p trouvé par posix_cpu_timer_del() est l'ancien leader ; à ce moment, p->sighand == NULL, la fonction considère que « la tâche est en train de se terminer, le chemin d'exit se chargera du retrait de la file », elle ne fait donc rien et retourne un succès. free_posix_timer() libère ensuite le k_itimer.
Point clé : exec() diffère de exit() — lors d'un exec(), le TGID ne change pas ; les minuteurs armés attachés au niveau du processus (p->signal->cpu_timers) sont hérités et restent en file. Ainsi :
run_posix_cpu_timers() (qui parcourt la timerqueue à chaque tick) accède au timerqueue_node d'un objet déjà libéré → UAF en lecture/écriture ;Des problèmes du même type existent également dans :
posix_cpu_timer_set() : un minuteur ordinaire se contente de retourner -ESRCH ; mais do_cpu_nanosleep(), interne au noyau, utilise un k_itimer alloué sur la pile — c'est la même UAF.posix_cpu_timer_rearm() : le réarmement échoue silencieusement et le minuteur n'expire plus (bug fonctionnel).Frederic Weisbecker a fait remarquer que dans __exit_signal(), tsk->sighand = NULL est un simple stockage ; sur les architectures à mémoire faiblement ordonnée comme ARM64, lorsque posix_cpu_timer_del() observe sighand == NULL, il n'est pas garanti qu'il observe les écritures de retrait de la file antérieures à posix_cpu_timers*_exit(), ce qui peut faire déclencher à tort WARN_ON_ONCE(timer_queued(tmr)).
__exit_signal(), remplacer par smp_store_release(&tsk->sighand, NULL) ;smp_acquire__after_ctrl_dep() sur le chemin !sighand de lock_task_sighand() ;timer_lock_sighand() : recherche la tâche + verrouille le sighand ; si sighand == NULL, il ne retourne pas mais réessaie la recherche — dans le scénario exec, le nouveau leader est trouvé ; dans le scénario exit, on n'abandonne qu'en cas d'échec de la recherche ;_del / _set / _rearm) utilisent désormais toutes ce helper.Le diff complet se trouve dans patches/920f893f735e.patch.
Un déclencheur de course est fourni dans poc/ : deux threads exécutent chacun des boucles à haute intensité :
timer_create(CLOCK_PROCESS_CPUTIME_ID) → arm (temps d'expiration initial très court) → attente active du déclenchement → timer_delete() ;fork() → dans le processus fils, crée un thread non-leader, et c'est ce thread qui appelle execve() (l'exec par un thread non-leader est la condition nécessaire de cette vulnérabilité) ; le processus parent le récupère immédiatement avec waitpid().Lorsque timer_delete() gagne la course contre de_thread()/__exit_signal(), sur un noyau non corrigé, le k_itimer déjà libéré reste sur le rbtree de signal->cpu_timers ; ensuite, run_posix_cpu_timers() ou d'autres opérations sur la timerqueue accèdent au nœud pendant. Avec un noyau KASAN, on observe de manière stable des rapports du type BUG: KASAN: use-after-free in run_posix_cpu_timers / timerqueue_del, etc. ; sans KASAN, cela se manifeste généralement par des avertissements noyau sporadiques ou un panic.
Nature du contenu : il s'agit d'un pur déclencheur de course en C, sans aucun primitif d'exploitation tel que heap spray, placement d'objets ou contrôle de RIP. Le transformer en exploit d'élévation de privilèges demanderait un travail considérable (heap feng shui, objet de remplissage dans la slab cache contenant
k_itimer, contournement de KASLR/CFI, etc.), et il est étroitement lié à la compilation spécifique du noyau. Ce dépôt exclut délibérément cette partie.
├── README.md ← 本文
├── patches/
│ └── 920f893f735e.patch ← mainline 修复补丁全文
└── poc/
├── cve_2026_64560_poc.c ← 触发器源码(Linux/Android 通用)
├── Makefile ← Linux / NDK 交叉编译
└── Android.mk ← NDK ndk-build(可选)
cd poc
make # 生成 cve_2026_64560_poc
sudo ./cve_2026_64560_poc -d 60
# 观察 dmesg: sudo dmesg -wH | grep -iE 'kasan|use-after|BUG|WARNING'
cd poc
export ANDROID_NDK_HOME=/path/to/ndk
make android # 生成 cve_2026_64560_poc_arm64(static, pie)
adb push cve_2026_64560_poc_arm64 /data/local/tmp/cvepoc
adb shell chmod 755 /data/local/tmp/cvepoc
adb shell /data/local/tmp/cvepoc -d 120
# 观察内核日志:
adb shell su 0 dmesg -w | grep -iE 'kasan|use-after|BUG|WARNING|timer'
# 无 root 时也可在触发崩溃后用 adb shell cat /sys/fs/pstore/console-ramoops* 查看
L'appareil doit satisfaire aux conditions suivantes :
CLOCK_PROCESS_CPUTIME_ID cible le TGID → il est rattaché à signal->cpu_timers et hérité après un exec — c'est la condition préalable de l'UAF (CLOCK_THREAD_CPUTIME_ID ne convient pas) ;switch_leader() fait que l'ancien leader renvoyé par pid_task(TGID) passe immédiatement à sighand = NULL — c'est la condition nécessaire de la course ;timer_create/arm/delete à haute fréquence et de fork/exec à haute fréquence en parallèle maximise la probabilité que posix_cpu_timer_del() tombe dans la fenêtre ; le traitement de l'expiration du minuteur (run_posix_cpu_timers) touche lui-même le nœud pendant, sans déclenchement supplémentaire.# Android:
adb shell cat /proc/version # 内核版本是否 >= 上表修复版本
adb shell getprop ro.build.version.security_patch # SPL 是否 > 2026-08
# Linux:
uname -r
# 或直接检查源码是否包含 timer_lock_sighand:
grep -r timer_lock_sighand /usr/src/linux/kernel/time/posix-cpu-timers.c
Si le PoC tourne plusieurs minutes sans aucun KASAN/panic et que le noyau est ≥ la version corrigée, l'appareil est considéré comme corrigé (le PoC dispose également d'un mode --check pour une validation légère de type smoke test).
Ce dépôt est exclusivement destiné à la recherche en sécurité et à la validation défensive. Ne l'exécutez que sur des appareils dont vous êtes propriétaire ou pour lesquels vous disposez d'une autorisation écrite. Le PoC peut entraîner une instabilité du noyau, voire un panic — ne l'exécutez pas sur des appareils de production. L'auteur décline toute responsabilité en cas d'utilisation abusive.
| Branche | Affectée | Version corrigée (≥) | Commit de correctif 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 |