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 B's and D's '..' 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)"
}