0/1 已展开

LLM 分析

ntfs3 KASAN slab-UAF 的 syzkaller 复现调试 patch

系列概况

  • 标题: [BUG] KASAN: slab-use-after-free Read in ntfs_fileattr_get
  • 作者: Jaeyoung Chung
  • 版本: 无(单封邮件,非系列)
  • 规模: 1 封邮件;patch 修改 2 个文件,4 处 hunk
  • 修改文件: fs/ntfs3/super.ckernel/sched/membarrier.c
  • 代码统计: 全部为新增 static 计数变量与基于 current->comm 前缀匹配的调试分支,无既有逻辑改动
  • Message-ID: 20260824151736.2243590-1-jjy600901@snu.ac.kr
  • 完整性: 邮件正文在 KASAN slab object 转储片段(ffff88810b7ad380: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc)之后被截断,仅看到开头一句 "Hello, We found a "KASAN: slab-use-after-free Read in ntfs_fi...";repro 脚本、syzrepro0 / syzrepro8 协同方式、完整调用栈均缺失

补丁目的

作者在 NTFS3 中复现到一条 KASAN slab-use-after-free Read,栈底指向 ntfs_fileattr_get。这份 patch 并不是直接修复,而是在 ntfs3 的 reconfigure/free 路径与 membarrier initcall 路径里插入 syzkaller 复现用调试桩:用 comm 名前缀区分复现器进程,对匹配进程做计数 + 阈值扩张,并在 membarrier 初始化尾部插入 mdelay(1),把一闪而过的竞争窗口人为放大,方便后续稳定复现与 bisect。

旧流程的问题

原始代码在这三处关键点没有观测手段:UAF 触发极不稳定,syzkaller 随机命中一次之后再次复现需要等待长时间的 race 窗口,CI / 本地 bisect 几乎不可行。

新流程

通过添加基于 current->comm 前缀匹配的 if 分支,仅让 syzrepro0 / syzrepro8 进程在关键路径上进入计数 + 延迟逻辑;正常用户进程不受影响,而复现器进程每次都会在固定点停留并留下计数指纹,便于把竞争窗口稳定下来。

关键实现

/* fs/ntfs3/super.c -- 新增静态计数器(放在 ntfs_fs_parse_param 之后) */
static unsigned long syz_rg_swaps;
static unsigned long syz_rg_swap_next = 1;
static unsigned long syz_rg_frees;
static unsigned long syz_rg_free_next = 1;

/* ntfs_fs_reconfigure 开头插入 */
if (!strncmp(current->comm, "syzrepro8", 10)) {
    syz_rg_swaps++;
    if (syz_rg_swaps == syz_rg_swap_next)
        syz_rg_swap_next = syz_rg_swaps + 400;
}

/* ntfs_fs_free 开头插入 */
if (!strncmp(current->comm, "syzrepro8", 10)) {
    syz_rg_frees++;
    if (syz_rg_frees == syz_rg_free_next)
        syz_rg_free_next = syz_rg_frees + 400;
}
/* kernel/sched/membarrier.c */
#include <linux/delay.h>

static unsigned long syz_rg_stalls;
static unsigned long syz_rg_stall_next = 1;

/* membarrier_init 末尾(initcall 路径) */
if (!strncmp(current->comm, "syzrepro0", 10)) {
    syz_rg_stalls++;
    if (syz_rg_stalls == syz_rg_stall_next)
        syz_rg_stall_next = syz_rg_stalls + 5000;
    mdelay(1); /* busy-wait 1ms,仅复现器进程触发 */
}

阈值扩张(swap_next = +400free_next = +400stall_next = +5000)保证计数只在罕见里程碑处触发自检,避免每次进入都打印日志;mdelay(1) 是同步忙等 1ms,专为把 membarrier init 与后续 ntfs3 操作之间拉开可观察的时序间隔。

                syzkaller reproducers
              +-------------------+
              |   syzrepro0 /     |
              |   syzrepro8       |
              +---------+---------+
                        |
                        | comm-prefix match
                        | (strncmp == syzrepro0/8)
                        v
        +---------------+---------------+
        |               |               |
        v               v               v
  membarrier_init  ntfs_fs_reconfigure  ntfs_fs_free
  +-------------+  +----------------+  +-------------+
  | syz_rg_stall|  | syz_rg_swap    |  | syz_rg_free |
  | + mdelay(1) |  | threshold +400 |  | thresh +400 |
  +------+------+  +-------+--------+  +------+------+
         |                |                   |
         +----------------+-------------------+
                          |
                          v
                  +-------+-------+
                  | KASAN report  |
                  | UAF in        |
                  | ntfs_fileattr_|
                  | get           |
                  +---------------+

类比

这就像在怀疑漏水的管道里塞了几只电子流量计和一只慢速阀:在容易引起水锤的几个开关上加装 "每经过一次就记一次、并在关键节点停 1ms 留出观察时间" 的装置,让原本一闪而过的泄漏慢放到肉眼可辨。mdelay(1) 相当于在两个阀门之间人为加一段 1ms 的停顿,让水锤的波形可以被压力传感器捕获;comm 前缀匹配则像给流量计贴标签,只有标记过的实验水龙头(syzrepro 进程)会触发,正常住户用水不受影响。

Highlight:风险与注意点

  • mdelay(1) 是 busy-waitmembarrier_init 是 boot 期 initcall,对所有依赖该 initcall 顺序的子系统都会额外耗掉 1ms x N 次;这种代码绝不应进入主线,仅适用于 syzkaller 调试构建。
  • comm 名匹配耦合到 syzkaller 内部命名约定"syzrepro0""syzrepro8" 是 syzkaller 复现器生成的固定 comm;如果 syzkaller 升级改了命名约定,patch 会瞬间失效。
  • 这不是真正的修复 patch:UAF 根因仍未定位,需要后续用完整 KASAN stack(释放点 + 读取点的 backtrace)、kmemleakslab_debug=UF 一起追到真正的释放路径。
  • 邮件正文被截断:KASAN 完整调用栈、syzrepro 脚本、syzrepro0syzrepro8 的协同方式都未给出,maintainer 难以独立复现。
  • 混入生产路径的味道fs/ntfs3/super.ckernel/sched/membarrier.c 都是热路径,把调试桩直接 commit 而不挂 CONFIG_DEBUG_* 开关,代码评审应当拒绝。

一句话总结

这是一份针对 NTFS3 ntfs_fileattr_get slab-UAF 的 syzkaller 复现调试 patch:在 ntfs3 reconfigure/free 与 membarrier init 中按 comm 名前缀匹配插入计数与 mdelay(1),仅用于放大竞争窗口并稳定复现,本身并不是真正的修复。