导出到用户空间的 drm_sched_fence 会保留一个指向产生它的 drm_gpu_scheduler 的裸指针。在 drm/msm 中,该调度器内嵌于 struct msm_ringbuffer 和 struct msm_gem_vm 中,而这两个结构体都是用普通的 kfree() 释放的。因此,用户空间持有的已完成 fence 的生命周期会超过其调度器,之后一次 SYNC_IOC_FILE_INFO——一个非特权 ioctl——会使 drm_sched_fence_get_timeline_name() 解引用已释放的 slab 内存。
BUG: KASAN: slab-use-after-free in drm_sched_fence_get_timeline_name
Read of size 8
drm_sched_fence_get_timeline_name() 的实现是 return fence->sched->name;。
调度器 fence 操作保留了一个 .release 回调,因此 fence 在发出信号时不会与操作分离。通过 sync_file 或 drm_syncobj 导出的已发出信号的 fence,在 ring 或 VM 被释放后仍指向内嵌的调度器。根据 dma-fence 生命周期约定,导出者必须让支撑已发出信号 fence 的数据在 RCU 宽限期内保持存活;drm/msm 没有做到这一点。
| 修复 | 01c8d1f385f7 — drm/msm: RCU-free the scheduler-containing ring and VM objects |
| 引入于 | 1d8a5ca436ee(drm/msm: Conversion to drm scheduler)、92395af63a99(drm/msm: Add VM_BIND submitqueue) |
| 已回移植 | 6.18-stable、7.2-stable(Greg Kroah-Hartman,2026-09-22) |
| 可达性 | 非特权——对 fence fd 调用 SYNC_IOC_FILE_INFO |
| 影响 | 对已释放 slab 的 UAF 读取(信息泄露/对已释放对象 name 指针的任意读取) |
该修复将两个包含对象的 kfree() 替换为 kfree_rcu():
- kfree(vm);
+ kfree_rcu(vm, rcu);
...
- kfree(ring);
+ kfree_rcu(ring, rcu);
该缺陷位于通用的 drm/sched 生命周期处理中,因此使用 KUnit mock 调度器即可在没有 Adreno 硬件的情况下复现。这就是 evidence/kasan-report.txt 中的证据。
cd poc && ./run-repro.sh # applies 0001-…patch, builds KUnit + KASAN, runs
poc/0001-drm-sched-kunit-uaf-repro.patch — 与硬件无关的 KUnit 复现器(经 KASAN 确认)poc/poc_userspace_drm_sched_uaf.c — 在受影响驱动上执行的非特权用户空间 ioctl 序列poc/kunitconfig、poc/run-repro.sh — 构建/运行测试框架evidence/kasan-report.txt — KASAN slab-use-after-free 报告poc_userspace_drm_sched_uaf.c 以 AMD XDNA 作为示例编写,因为该设备当时可用;相同的形态也适用于 msm(MSM_SUBMITQUEUE / VM_BIND 通过 sync_file_create() + fd_install() 导出一个已完成的 fence)以及 nouveau。
同一类 dma-fence 生命周期问题也在核心中得到了修复:
3ed11c671ff7
— dma-buf/dma-fence: fix checking signaling bit for timeline and driver name v3
(作者 Christian König;由本仓库作者报告并测试)。
由 JongHyuk Kim(MalHyuk) 发现并修复 — [email protected]。
补丁由 Rob Clark 和 Greg Kroah-Hartman 签署。
Patchwork:https://patchwork.freedesktop.org/patch/750234/
复现器代码为 GPL-2.0,与其所派生自的内核源代码一致。