⬢ DragonFlyBSD Kernel Audit
DF-3116 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3116",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "Code-certain TOCTOU: msdosfs_rename saves de_fndoffset/de_fndcnt (vnops:1009-1010), drops the target-dir lock across doscheckpath (vnops:1045) or its source relookup (vnops:1127), then restores the stale slot (vnops:1186-1187) into createde() which writes DE_EXTERNALIZE with no revalidation (lookup.c:644-646), overwriting any dirent a concurrent create placed on the same deterministic first-fit slot. 10 guest runs (~1e6 window-opening renames, windows widened to ~200us via a 100-deep doscheckpath walk) never landed the exact overwrite interleaving (lock-handoff wakeup latency); detection was namecache-blind stat() in early runs, readdir()+rotation-unlink in later runs, zero hits. The race family provably fires (kernel logged kern_rename: retry due to race), and racing this path reproducibly wedges the system \u2014 verified and filed separately as DF-3117. FreeBSD rewrote msdosfs_rename (2019) for this same stale-offset class.",
  "exploit_chain": "",
  "evidence": [
    "findings/poc/DF-3116/VERDICT.md \u2014 full attempt matrix and code trace",
    "findings/poc/DF-3116/run.10.wedge.log \u2014 clean 38.6k dir renames + 100k creates, zero theft, umount wedge",
    "findings/poc/DF-3116/fix.diff \u2014 createde revalidation (compile-verified, msdos.ko -Werror)"
  ],
  "kernel_refs": [
    "sys/vfs/msdosfs/msdosfs_lookup.c:367-400",
    "sys/vfs/msdosfs/msdosfs_lookup.c:611-646",
    "sys/vfs/msdosfs/msdosfs_vnops.c:1009-1010",
    "sys/vfs/msdosfs/msdosfs_vnops.c:1045",
    "sys/vfs/msdosfs/msdosfs_vnops.c:1127-1128",
    "sys/vfs/msdosfs/msdosfs_vnops.c:1186-1188",
    "sys/kern/vfs_default.c:953-1062",
    "sys/kern/vfs_default.c:312-378"
  ],
  "poc_changes": "Seed sketch had no code; authored from scratch. Iterations: (1) stat()-based vanish detector was namecache-blind -> switched to readdir() (reads dirents via buffer cache) + rotation-unlink ENOENT detector; (2) 512-byte path buffers truncated the 100-deep chain path (my bug, kernel was fine) -> 1100-byte buffers; (3) Window B (same-dir file rename, ~100ns unlock-relock gap) never loseable to wakeup latency -> Window A cross-parent DIRECTORY renames with doscheckpath widened by a 100-deep .. chain (~200us windows); (4) alternating two-name bounce replaced by unique-name one-directional bounces so every rename takes the EJUSTRETURN path.",
  "attempts": 10,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "runtime_sec": 2400,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh   (cc -O2 -Wall -o trigger trigger.c)",
  "run_cmd": "sh run.sh     (root; mounts 512MB FAT32 on /dev/vn0; racers run as nobody); sh wedge.sh for the attribution-clean probe",
  "code_hash": "15d8f4155d55924053327ae196acb62fe4a18a8285d0dd0c3a5697e88dc7e1b2",
  "notes": "Fix compile-verified only (no runtime A/B possible: baseline theft not reproduced). The verified DoS consequence of the same racing surface is DF-3117 (reproduced).",
  "recommended_fix": "createde(): revalidate that every slot in [de_fndoffset - de_fndcnt*32, de_fndoffset] still reads SLOT_EMPTY/SLOT_DELETED before writing; return EEXIST if stolen (fix.diff).",
  "fix_status": "not_testable",
  "fix_kernel_uname": "",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patch applied to guest /usr/src copy; sys/vfs/msdosfs module built clean (msdos.ko, -Werror). Runtime A/B not meaningful because the baseline dirent theft never reproduced on this guest; source restored afterward.",
  "fix_evidence": "fix.diff; build ran 2026-09-06 on guest pid-253541 session (msdos.ko artifact removed after restore)"
}