⬢ DragonFlyBSD Kernel Audit
DF-3037 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3037",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "dos",
 "confidence": "certain",
 "verdict": "msdosfs_rename() calls doscheckpath() (vnops.c:1045) whose ancestor walk (lookup.c:822-863) has no iteration bound; on a crafted FAT image whose rename-destination .. chain cycles without passing through the root or source cluster, the walk never terminates (after the first pass every bread() is a cache hit and every deget() a hash hit, so it never sleeps). Verified on the stock INVARIANTS guest: control rename returns in 0s; with a two-directory B<->D .. cycle installed, rename(2) never returned, kill -9 was never delivered, and the whole 6-CPU guest stopped servicing ssh (banner-exchange timeouts) with no panic on serial \u2014 a system-wide livelock requiring reboot. Bound-the-walk fix.diff validated: patched kernel #1 returns EINVAL for the cycle rename in <1s and the guest stays fully responsive; control rename unaffected. Current FreeBSD has the same unbounded loop, so this is an upstream-live hardening gap.",
 "exploit_chain": "craft FAT16 image (16MB, newfs_msdos) with dirs A/A/moveme/B/D -> patch the 16-bit deStartCluster of Bs and Ds .. entries to point at each other (dirpatch) -> root mounts rw (vnconfig+mount_msdos; or user with vfs.usermount=1 owning the image) -> any user with write access runs rename(/mnt/A/moveme, /mnt/B/m2) -> kernel spins forever in doscheckpath: unkillable thread + token-churn livelock -> guest-wide DoS until reboot",
 "evidence": [
  "run.log: control rename rename returned success ... elapsed=0s; after cycle patch TRIGGER STILL RUNNING AFTER 15s, KILL -9 INEFFECTIVE (stuck in kernel) -- DoS CONFIRMED",
  "run.2.log: post-trigger ssh probe exits 255 Connection timed out during banner exchange (guest no longer services logins); boot.log serial console shows no panic",
  "run.fixed.log: patched kernel - rename: Invalid argument in <1s, process exits, guest responsive afterwards",
  "fix.diff: depth cap 4096 in doscheckpath, applies cleanly (patch --dry-run verified) and builds (nativekernel BUILD_RC=0)"
 ],
 "kernel_refs": [
  "sys/vfs/msdosfs/msdosfs_vnops.c:1045",
  "sys/vfs/msdosfs/msdosfs_lookup.c:822",
  "sys/vfs/msdosfs/msdosfs_lookup.c:861"
 ],
 "poc_changes": "none vs seed (pack authored fresh in this run; no seed existed)",
 "attempts": 2,
 "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 (baseline) / see fix_kernel_uname",
 "runtime_sec": 1150,
 "guest_dirty": 0,
 "build_cmd": "cc -O -Wall -o /root/df3037/dirpatch /root/df3037/dirpatch.c && cc -O -Wall -o /root/df3037/trigger /root/df3037/trigger.c",
 "run_cmd": "sh /root/df3037/run.sh",
 "code_hash": "9d8e9788efecd70b38f9cba0738c2f4ee1ebeadd95ae6fb8c4565d4f6a6be5a7 (trigger.c); 6edeed7ef7e1d8c1fedd7ce19ff8b6f68aed974001f78306ddf1d0f898b8db3c (dirpatch.c); 1ea3498350ec3a1ea516d6bd556bab205b4ab5b4b52ccd1adf3254a05865b7ea (run.sh); 2def247f9cbd436b6ae4950cef6429b411d61629d13e499fb62c4ffc98b5adb7 (fix.diff)",
 "notes": "rename(2) is the only caller of doscheckpath; requires rw mount of the crafted image (root, or vfs.usermount=1 owner). Upstream FreeBSD sys/fs/msdosfs/msdosfs_lookup.c doscheckpath has the identical unbounded for(;;) (checked 2026-09). Baseline wedge dirtied the guest; guest was reset (vm.sh reset with-src) before and after fix validation.",
 "recommended_fix": "Bound the doscheckpath .. ancestor walk (depth cap 4096 -> EINVAL); see fix.diff.",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sat Sep 5 15:02:31 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Rebuilt X86_64_GENERIC with fix.diff (doscheckpath depth cap 4096 -> EINVAL; patch -p1 applied cleanly, make nativekernel BUILD_RC=0, installkernel INSTALL_RC=0, kernel #1 booted). Baseline kernel #0 (same PoC, run.log): cycle-rename hangs forever, kill -9 ineffective, guest-wide ssh outage. Patched kernel #1 (run.fixed.log): identical crafted image + identical rename returns rename: Invalid argument (EINVAL) in <1s, trigger exits rc=1, guest fully responsive (uptime/load normal, run.sh completes RUN_RC=0); control rename on unpatched image still succeeds in 0s. Bad behavior gone.",
 "fix_evidence": "run.fixed.log (control OK + cycle rename EINVAL + responsive guest); fix_build.log (build/install completion tails, BUILD_RC=0/INSTALL_RC=0); fix.diff applies cleanly (patch -p1 verified in-guest and out-of-guest)"
}