针对 CVE-2025-38352 的可复现概念验证(PoC)及自动化 GDB 编排脚本。该漏洞是 Linux 内核 POSIX CPU 定时器子系统(kernel/time/posix-cpu-timers.c)中的检查时间与使用时间(TOCTOU)竞态条件,可导致释放后使用(UAF)。
免责声明: 本项目仅用于教育、防御及安全研究目的。所有测试与复现均在隔离的 ARM64 QEMU 虚拟机中进行。
进程 A 创建子进程 B。
进程 B 创建一个 CPU 定时器并开始退出,进入僵尸状态。就在 B 退出时,其 CPU 核心上收到一个定时器中断。内核进入中断上下文处理该定时器,但为避免死锁而临时释放其锁(unlock_task_sighand)。
与此同时,在另一个核心上,进程 A 回收正在退出的 B,清理其信号处理器(sighand = NULL)并取消其 PID 的哈希。
B 中的另一个线程调用定时器删除(posix_cpu_timer_del)。它尝试通过 PID 查找该任务并检查其信号处理器,但发现它们已被清理 / 为 NULL。它误以为该任务已完全死亡且不可能有定时器触发,于是认为安全并将该定时器从内存中释放。
与此同时,第一个核心上的定时器中断处理器恢复执行,触发已过期的定时器。由于该定时器刚在第 4 步被释放,访问它会触发释放后使用(UAF)并使内核崩溃。
poc_gdb_script.py)由于 QEMU 的 GDB stub 不支持 set non-stop on(暂停一个 vCPU 会暂停所有 vCPU),因此无法通过普通断点实现标准的多线程同步。
本仓库通过内存指令修补解决该问题:
阶段 1(CPU 屏障):
exit_notify(CPU 2)kernel_wait4(CPU 0)__arm64_sys_timer_delete(CPU 1)handle_posix_cpu_timers(CPU 2)0x14000000(b . 无限循环)修补其 $pc 并恢复执行。阶段 2(顺序编排):
set scheduler-locking on)并按顺序单步推进每个 vCPU:
handle_posix_cpu_timers 推进越过 unlock_task_sighand()。kernel_wait4 推进越过 tsk->sighand = NULL。在提交 1bf1aa362e6b9573a310fcd14f35bc875b42ba83 处下载或克隆 Android Common Kernel 源码树:
# Option A: Download tarball directly
curl -LO https://android.googlesource.com/kernel/common/+archive/1bf1aa362e6b9573a310fcd14f35bc875b42ba83.tar.gz
mkdir -p kernel-cve && tar -xzf 1bf1aa362e6b9573a310fcd14f35bc875b42ba83.tar.gz -C kernel-cve
cd kernel-cve
# Option B: Clone repository
git clone https://android.googlesource.com/kernel/common
cd common
git checkout 1bf1aa362e6b9573a310fcd14f35bc875b42ba83
run_posix_cpu_timers() 中的补丁打开 kernel/time/posix-cpu-timers.c,定位函数 run_posix_cpu_timers()(约第 1435–1448 行),并注释掉补丁行:
void run_posix_cpu_timers(void)
{
struct task_struct *tsk = current;
lockdep_assert_irqs_disabled();
/*
* Ensure that release_task(tsk) can't happen while
* handle_posix_cpu_timers() is running. Otherwise, a concurrent
* posix_cpu_timer_del() may fail to lock_task_sighand(tsk) and
* miss timer->it.cpu.firing != 0.
*/
// if (tsk->exit_state)
// return;
CONFIG_POSIX_CPU_TIMERS_TASK_WORK注意: 当
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y时,CPU 定时器从task_work上下文而非定时器 IRQ 执行,从而绕过此竞态条件。因此,必须禁用它才能复现该漏洞。
由于 CONFIG_POSIX_CPU_TIMERS_TASK_WORK 在上游 Kconfig 中缺少提示字符串,它默认为 y 且无法在 menuconfig 中直接切换。你可以按如下方式将其暴露出来:
kernel/time/Kconfig(约第 56 行)中,添加提示字符串并更改默认值:
config POSIX_CPU_TIMERS_TASK_WORK
bool "POSIX CPU timers task work"
default n
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig
scripts/config --disable POSIX_CPU_TIMERS_TASK_WORK
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make olddefconfig
.config 中已被禁用:
grep POSIX_CPU_TIMERS_TASK_WORK .config
# Expected output: # CONFIG_POSIX_CPU_TIMERS_TASK_WORK is not set
编译 ARM64 内核镜像:
time ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j$(nproc) Image
交叉编译 poc.c:
aarch64-linux-gnu-gcc -ggdb3 ./poc.c -o poc
启动具有 4 个 vCPU(-smp 4)的 QEMU 虚拟机,并启用 GDB stub(-s 标志,端口 1234):
qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-smp 4 \
-m 2G \
-kernel arch/arm64/boot/Image \
-append "console=ttyAMA0 root=/dev/vda oops=panic panic_on_warn=1" \
... \
-s
在宿主机终端中,将 GDB 附加到 QEMU 并加载编排脚本:
gdb-multiarch vmlinux
pwndbg> target remote :1234
pwndbg> source poc_gdb_script.py
pwndbg> continue
在 QEMU 客户机 shell 中,运行编译好的二进制文件:
./poc
[!TIP] 时序与故障排除:
如果定时器在目标线程到达僵尸状态之前过早触发,GDB 将记录:[!] ERROR: handle_posix_cpu_timers fired before exit_state == EXIT_ZOMBIE
- 选项 1: 只需重新运行
./poc并多次 source GDB 脚本。- 选项 2: 增大
poc.c中的TIMER_FIRE_MS(例如从#define TIMER_FIRE_MS 10改为20),重新编译poc.c并再次运行。这为线程提供了额外时间,使其在定时器过期前到达exit_notify()并转换为EXIT_ZOMBIE。
https://github.com/user-attachments/assets/11481b5c-25f8-46c3-be3a-7b79a06a4948
timer_delete 推进至调用 release_posix_timer()(释放 sigq)。