DF-3116 / verdict.json
{ "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)" } |