⬢ DragonFlyBSD Kernel Audit
DF-5171 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5171",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "vcdstrategy() panics on any buffer command other than READ/WRITE (vcd.c:154-167); HAMMER delivers BUF_CMD_FLUSH on mount recovery and every flusher cycle, so mounting a HAMMER filesystem on a vcd-backed volume kills the vkernel inside mount(2). Reproduced on the guest with a purpose-built VKERNEL64: panic: vcd: bad b_cmd 5 at uptime 3s (run.log:45-46) with the fatal bio instrumented as cmd=5 off=0 bcount=0 - exactly hammer_io_flush_sync()s shape. The vdisk sibling handles FLUSH (vdisk.c:332-337); vcd never got the 2016 rework. Scope: crash/DoS of the vkernel userspace process (guest-root trigger); no host-kernel impact. Currently gated behind DF-1031 (-c crashes in init_disk before any vcd exists) - verified live the moment that two-line bug is fixed.",
  "exploit_chain": "vkernel booted with -c <HAMMER-formatted image> (requires DF-1031 fixed/enabled) -> guest root: mount(\"hammer\", \"/mnt\", rw, volumes={\"/dev/vcd0\"}) -> hammer mount recovery calls hammer_io_wait_all(...,1) -> hammer_io_flush_sync() builds BUF_CMD_FLUSH bio (b_bcount=0, bio_offset=0) -> vn_strategy(devvp) -> devfs_spec_strategy passthrough (b_cmd!=READ/WRITE shortcut) -> diskstrategy -> dscheck passes (0/0 is aligned+in-bounds; dscheck never filters by b_cmd) -> dev_dstrategy(vcd0 rawdev) -> vcdstrategy() -> default: panic(\"vcd: bad b_cmd 5\"). DoS of the vkernel instance.",
  "evidence": "run.log:34-46 (MOUNTING -> VCDDBG cmd=5 off=0 bcount=0 -> panic: vcd: bad b_cmd 5, Uptime 3s, backtrace); panic.txt (one-line signature); fix_run.log:35-40 (patched: mounted clean, mount(hammer) returned OK, RESULT: no-panic (STILL ALIVE after 15s)); fix.diff (validated); VERDICT.md sec.4 (FFS-TRIM route proven dead: ffs_alloc.c:1673 tests the devfs mounts flags).",
  "kernel_refs": [
    "sys/dev/virtual/vkernel/cdrom/vcd.c:154",
    "sys/dev/virtual/vkernel/cdrom/vcd.c:165",
    "sys/vfs/hammer/hammer_io.c:1985",
    "sys/vfs/hammer/hammer_io.c:1995",
    "sys/vfs/hammer/hammer_flusher.c:784",
    "sys/vfs/hammer/hammer_flusher.c:910",
    "sys/vfs/hammer/hammer_recover.c:1438",
    "sys/kern/subr_diskslice.c:91",
    "sys/kern/subr_disk.c:1246",
    "sys/dev/virtual/vkernel/disk/vdisk.c:332",
    "sys/platform/vkernel64/platform/init.c:487"
  ],
  "poc_changes": "Seed sketch replaced entirely: wrote a static PID1 binary (vcd_flush_poc.c) that mounts HAMMER rw on /dev/vcd0 via mount(2) with struct hammer_mount_info (verbatim layout from hammer_mount.h); logging goes to /dev/console (inits stdio is dead in the vkernel) plus /marker. Harness mirrors DF-5010s: config VKERNEL64 + build-id link workaround, root.img UFS with the PoC, images built guest-side (newfs_hammer -f -L for a 2GB volume; dd+sync ordering matters: flush the zero-fill BEFORE formatting or late zero pages clobber the volume header). Two guest-local test-enablers in the guests /usr/src only: (1) DF-1031 NULL-flags guard so -c can create a vcd at all, (2) CD backing opened O_RDWR so a rw mount is possible (vcd carries physwrite; vkd -r opens O_RDWR the same way). FFS-TRIM route (vcd_trim_poc.c) explored first and killed: the MNT_TRIM gate reads the devfs mounts flags (ffs_alloc.c:1670-1677) so no FREEBLKS bio is ever issued - documented in VERDICT.md sec.4 as a separate bug for ffs_alloc.c.",
  "attempts": 9,
  "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": 5400,
  "guest_dirty": 1,
  "build_cmd": "see build.sh (config VKERNEL64; make -j6; cc -static -O2 -o poc src/vcd_flush_poc.c; images via vnconfig/newfs/newfs_hammer -f)",
  "run_cmd": "cd /root/vk && cp root.pristine root.img && timeout 90 ./vkernel64 -m 256M -r root.img -c hammer.img -e init_path=/poc < /dev/null > run.log 2>&1",
  "code_hash": "133b7eb4998a6f1936528df3befa20b9be379677c52686507c8e67d3e648ce7c",
  "notes": "Impact is a panic of the vkernel (userspace process): guest-root DoS, no host privilege boundary - filed Low. Gated behind DF-1031 (init_disk diskFlags NULL deref) until that is fixed; PoC applies a documented two-line enabler. BONUS discovery for the orchestrator: FFS TRIM is dead code tree-wide - ffs_blkfree gates on ip->i_devvp->v_mount (devfs) instead of the UFS mount, so MNT_TRIM never produces FREEBLKS bios (ffs_alloc.c:1670-1677, empirically confirmed via statfs f_flags=01001040 + instrumented vcdstrategy showing only cmd=1/2); worth its own finding against ffs_alloc.c. Guest was reset with-src after the run.",
  "recommended_fix": "Handle BUF_CMD_FLUSH via fsync(sc->fd) mirroring vkd_doio (vdisk.c:332-337) and complete unknown buffer commands instead of panic(\"vcd: bad b_cmd\") - see validated fix.diff.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Single-fix kernel (only fix.diff applied to vcd.c) reboots the identical PoC to a clean HAMMER mount: mounted clean, no recovery needed, mount(hammer) returned OK, 15s of flusher cycles with no panic (fix_run.log:35-58, Uptime 18s vs 3s baseline panic). Baseline reproduced the panic immediately before and after (same images, same boot line).",
  "fix_evidence": "fix_run.log:35-40,58 (patched run); run.log:45-46 (baseline panic); fix.diff (unified diff, applied cleanly, byte-compared); fix_build.log (MAKE_RC=0)."
}