⬢ DragonFlyBSD Kernel Audit
DF-3107 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3107",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "The defect is code-certain and occurs deterministically at the instruction level: ufs_checkpath vput()s each intermediate ancestor from refcount 1 to 0 (ufs_lookup.c:1160) and immediately reads vp->v_mount (ufs_lookup.c:1161) - a use of an unreferenced, instantly-reclaimable (or already-reclaimed, under VREF_FINALIZE) vnode, once per rename of a directory into a target >= 3 levels deep. FreeBSD caches the mount pointer before the loop for exactly this reason; DragonFly (and its ext2_checkpath twin at ext2_lookup.c:1235) did not take that fix. The adverse effect requires another CPU to recycle and reinstall that exact vnode between the vput and the read; a bounded stress (20,000 deep renames on a UFS mount + 60,000 vnode churn cycles on the stock guest kernel) did not win the window: no crash, no anomalous dmesg, all renames succeeded. Filed as a correctness/lifetime bug (Medium) rather than a demonstrated exploit.",
  "exploit_chain": "",
  "evidence": [
    "findings/poc/DF-3107/stress.log: 20000 renames + 60000 churn cycles, no crash, guest up",
    "sys/vfs/ufs/ufs_lookup.c:1160-1161 (vput then vp->v_mount)",
    "sys/kern/vfs_lock.c:345 (vrele 1->0 leaves vnode cached/unreferenced; VREF_FINALIZE path terminates synchronously)"
  ],
  "kernel_refs": [
    "sys/vfs/ufs/ufs_lookup.c:1160",
    "sys/vfs/ufs/ufs_lookup.c:1161",
    "sys/vfs/ext2fs/ext2_lookup.c:1235",
    "sys/kern/vfs_lock.c:345"
  ],
  "poc_changes": "Original sketch used /tmp for the renames - /tmp is hammer2 on this guest, so ufs_checkpath is never called; rewrote the stress to build the hierarchy on a vn-backed UFS mount. Guest /bin/sh lacks $SECONDS; converted time-based loops to counters.",
  "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",
  "runtime_sec": 330,
  "guest_dirty": 0,
  "build_cmd": "(none - shell stress)",
  "run_cmd": "sh /root/df3107/stress.sh  (as root on guest)",
  "code_hash": "3926384bcfb09ff2686dd484872746cf0490afdbeb1fcd2a6921497e1decec92",
  "notes": "Race window is a few instructions wide; winning it needs vnode-recycle pressure synchronized into the window (e.g. vnlru bulk reclaim racing the rename). Reported for the deterministic lifetime violation and the FreeBSD divergence (they cache mp before the loop), not for a demonstrated crash. Fix is a two-line mount-pointer cache; same fix needed in ext2_checkpath.",
  "recommended_fix": "Cache mp = ITOV(target)->v_mount before the loop in ufs_checkpath and use VFS_VGET(mp, ...) after vput(vp); mirror in ext2_checkpath.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "No effect reproduces on an unpatched kernel (race not won), so a fix build cannot be meaningfully validated by observation; the two-line mount-cache fix is recommended from the code path and FreeBSD precedent."
}