Reproducer / PoC(触发验证型):Linux & Android (NDK) 本仓库仅用于在自有测试设备上验证补丁状态与研究学习,不包含任何提权/利用原语。
| 字段 | 内容 |
|---|---|
| CVE ID | CVE-2026-64560 |
| 标题 | posix-cpu-timers: Prevent UAF caused by non-leader exec() race |
| 类型 | Use-After-Free(CWE-416),竞态条件 |
| 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%(2nd percentile,2026-08 时点) |
| CISA KEV | 未收录 |
| 公开日期 | 2026-07-29 |
| 修复提交(mainline) | 920f893f735e92ba3a1cd9256899a186b161928d |
| 引入问题提交(Fixes:) | 55e8c8eb2c7b (v5.7, 2020) — "posix-cpu-timers: Store a reference to a pid not a task" |
| 修复者 | Thomas Gleixner <[email protected]> |
| 报告者 | Wongi Lee <[email protected]>, Jungwoo Lee <[email protected]> |
| 影响文件 | kernel/exit.c、kernel/signal.c、kernel/time/posix-cpu-timers.c |
漏洞引入于 v5.7(2020-05),修复已 backport 到各 stable 分支:
Android GKI 内核基于 5.10 / 5.15 / 6.1 / 6.6 / 6.12 LTS,均在受影响范围内。由于主线修复提交发布于 2026-07-29,2026-08-01 及之前 SPL(安全补丁级别)的 Android 设备基本都未合入该修复。可在设备上用 adb shell cat /proc/version 与 getprop ro.build.version.security_patch 确认内核版本和 SPL。
POSIX CPU 定时器(timer_create(CLOCK_PROCESS_CPUTIME_ID, ...) / timer_create(CLOCK_THREAD_CPUTIME_ID, ...))在内核中由 kernel/time/posix-cpu-timers.c 管理。每个 k_itimer 通过 it.cpu.pid 记住目标任务;操作定时器时需要 lock_task_sighand(p, &flags) 拿到该 task 的 sighand->siglock 以保护 timerqueue。
2020 年的提交 55e8c8eb2c7b 把定时器里缓存的 task 指针换成了 pid 引用(为了修 2010 年 workaround e0a70217107e 引入的问题),每次操作前做 pid_task(pid, type) 查找。这个改动留下了本 CVE 的竞态窗口。
execve() 由非 leader 线程发起时,de_thread() → switch_leader() 会让 TGID 从旧 leader 转移到新 leader,旧 leader 走 release_task() → __exit_signal(),其中会把 old_leader->sighand = NULL 并 unhash_task(old_leader)。
与此同时,另一个 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 被释放
posix_cpu_timer_del() 查到的 p 是旧 leader,此时 p->sighand == NULL,函数认为"任务正在退出,exit 路径会负责摘链",于是什么都没做就返回成功。随后 free_posix_timer() 把 k_itimer 释放。
关键点:exec() 与 exit() 不同——exec() 时 TGID 不变,挂在进程级(p->signal->cpu_timers)的 armed 定时器会被继承、仍然入队。于是:
run_posix_cpu_timers()(tick 中遍历 timerqueue)访问已释放对象的 timerqueue_node → UAF 读/写;同类问题也存在于:
posix_cpu_timer_set():普通定时器只是暂时返回 -ESRCH;但内核内部的 do_cpu_nanosleep() 使用栈上分配的 k_itimer,是同样的 UAF。posix_cpu_timer_rearm():静默 rearm 失败,定时器不再到期(功能性 bug)。Frederic Weisbecker 指出:__exit_signal() 中 tsk->sighand = NULL 是普通存储,在 ARM64 等弱序架构上,posix_cpu_timer_del() 观察到 sighand == NULL 时并不保证能观察到 posix_cpu_timers*_exit() 之前的摘链写,导致 WARN_ON_ONCE(timer_queued(tmr)) 可能误报。
__exit_signal() 中改为 smp_store_release(&tsk->sighand, NULL);lock_task_sighand() 的 !sighand 路径增加 smp_acquire__after_ctrl_dep();timer_lock_sighand():查 task + 锁 sighand,若 sighand == NULL 不返回而是重试查找——exec 场景下会查到新 leader,exit 场景下查找失败才放弃;_del / _set / _rearm)统一改用该 helper。完整 diff 见 patches/920f893f735e.patch。
poc/ 下提供一个竞争触发器:两个线程分别高强度循环
timer_create(CLOCK_PROCESS_CPUTIME_ID) → arm(很短的初始到期时间)→ 忙等触发 → timer_delete();fork() → 子进程中创建非 leader 线程,由该线程调用 execve()(非 leader exec 是本漏洞的必要条件),父进程立即 waitpid() 回收。当 timer_delete() 恰好与 de_thread()/__exit_signal() 竞争命中时,未打补丁的内核中已释放的 k_itimer 仍挂在 signal->cpu_timers 的 rbtree 上,之后 run_posix_cpu_timers() 或其他 timerqueue 操作将访问悬空节点。配合 KASAN 内核可稳定观测到 BUG: KASAN: use-after-free in run_posix_cpu_timers / timerqueue_del 等报告;无 KASAN 时通常表现为偶发内核警告或 panic。
性质说明:这是一枚纯 C 的 race 触发器,不包含 heap spray、对象占位、RIP 控制等任何利用原语。将其变成提权 exploit 需要额外的大量工作(堆风水、
k_itimer所在 slab cache 的占位对象、绕过 KASLR/CFI 等),且与具体内核构建强相关。本仓库刻意不包含这部分内容。
├── 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* 查看
需要设备满足:
CLOCK_PROCESS_CPUTIME_ID 定时器以 TGID 为目标 → 挂在 signal->cpu_timers,exec 后被继承 —— 这是 UAF 的前提(CLOCK_THREAD_CPUTIME_ID 不行);switch_leader() 使 pid_task(TGID) 返回的旧 leader 随即 sighand = NULL —— 这是竞态的必要条件;timer_create/arm/delete + 高频 fork/exec 并行,最大化 posix_cpu_timer_del() 落在窗口内的概率;定时器到期处理(run_posix_cpu_timers)本身也会触碰悬节点,无需额外触发。# 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
跑 PoC 数分钟无任何 KASAN/panic 且内核 ≥ 修复版本,即视为已修复(PoC 本身也带 --check 模式做轻量冒烟验证)。
本仓库仅面向安全研究与防御验证。仅在您拥有或获得书面授权的设备上运行。PoC 会诱发内核不稳定甚至 panic,请勿在生产设备上运行。作者不对任何滥用造成的后果负责。
| 分支 | 受影响 | 修复版本(≥) | 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 |