vcdstrategy() panics on BUF_CMD_FLUSH/BUF_CMD_FREEBLKS β mounting a HAMMER filesystem on a vcd-backed volume kills the vkernel inside mount(2)
Summary
The b_cmd switch handles only READ/WRITE - HAMMER delivers BUF_CMD_FLUSH on every mount recovery and flusher cycle, dscheck passes it through, and the vkernel panics 'bad b_cmd 5'. The vdisk sibling got FLUSH handling in the 2016 rework; vcd never did. REPRODUCED in a booted vkernel ('panic: vcd: bad b_cmd 5' at uptime 3s, instrumented fatal bio); FLUSH->fsync + complete-unknown fix validated (clean HAMMER mount, 15s flusher cycles, no panic). Currently gated behind the DF-1031 -c boot bug. BONUS routed: FFS TRIM is dead code tree-wide (ffs_alloc.c:1670 gates on i_devvp->v_mount which is devfs, never the UFS mount) - one-word fix ITOV(ip)->v_mount.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5171 Β· 18 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.0 KB | β raw | |
| VERDICT.md | β | 5.1 KB | β raw | |
| vcd_flush_poc.c | β | 2.3 KB | view raw | |
| vcd_trim_poc.c | β | 4.2 KB | view raw | |
| odcat.c | β | 843 B | view raw | |
| guest_prep.sh | β | 1.3 KB | view raw | |
| build.sh | β | 173 B | view raw | |
| run.sh | β | 160 B | view raw | |
| build.log | β | 8.4 KB | view raw | |
| fix_build.log | β | 433 B | view raw | |
| run.log | β | 2.4 KB | view raw | |
| fix_run.log | β | 2.2 KB | view raw | |
| panic.txt | β | 25 B | view raw | |
| env.txt | β | 462 B | view raw | |
| fix.diff | β | 844 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 5.4 KB | view raw | |
| hashes.txt | β | 392 B | view raw |
DF-5171 β vcdstrategy() panics on BUF_CMD_FLUSH / BUF_CMD_FREEBLKS
vkernel virtual CD-ROM driver β sys/dev/virtual/vkernel/cdrom/vcd.c
What
vcdstrategy() (vcd.c:136-177) handles only BUF_CMD_READ and
BUF_CMD_WRITE; every other buffer command hits:
default:
panic("vcd: bad b_cmd %d", bp->b_cmd); /* vcd.c:165 */
The vdisk sibling was reworked in 2016 and handles BUF_CMD_FLUSH
(vdisk.c:332-337, fsync() on the backing file); vcd never got that
treatment. Two in-tree producers deliver non-R/W bios to any block
device:
- HAMMER β
hammer_io_flush_sync()(sys/vfs/hammer/hammer_io.c:1985-2007) issues aBUF_CMD_FLUSHbio (b_bcount=0,bio_offset=0) throughvn_strategy(volume->devvp)on every mount-time recovery and every flusher cycle (hammer_io_wait_all(..., 1)from hammer_flusher.c:784,910 and hammer_recover.c:1438,1461,1465). - FFS with
mount -o trimβffs_blkfree()(sys/vfs/ufs/ffs_alloc.c:1668-1705) issuesBUF_CMD_FREEBLKS. (Killed during verification: the MNT_TRIM gate testsip->i_devvp->v_mountβ the devfs mount β so the TRIM branch is dead code in this tree; see VERDICT.md Β§4.)
Impact
A guest root inside the vkernel that mounts a HAMMER filesystem on a
vcd-backed volume (or would use FFS TRIM on one) panics the vkernel
instantly β inside the mount(2) syscall. The vkernel is a userspace
process on the host, so this is a crash/DoS of the vkernel instance, not
of the host kernel and not a privilege boundary on the host.
Currently gated behind DF-1031: on HEAD, init_disk(cdFile, NULL, β¦, VKD_CD)
passes diskFlags == NULL and dereferences diskFlags[i]
(sys/platform/vkernel64/platform/init.c:487-489 + init_disk body), so the
-c option kills the vkernel before any vcd device can exist. The moment
that crash is fixed (a two-line fix), this bug is live: the PoC boots a
vkernel with that enabler applied and panics on the first HAMMER flush.
Verified on the guest (2026-09-30)
- Baseline:
panic: vcd: bad b_cmd 5at uptime 3s, insidemount(2)β run.log:45-46, backtrace included. (That build also carried a temporary per-bio kprintf which recorded the fatal bio verbatim:VCDDBG: bio cmd=5 off=0 bcount=0 dev=vcd0β exactly thehammer_io_flush_syncshape.) - Fix validated: with
fix.diff(BUF_CMD_FLUSH βfsync(sc->fd), other commands β complete instead of panic) the same PoC printsmount(hammer) returned OKandRESULT: no-panic (STILL ALIVE after 15s)β fix_run.log:39-40 β and HAMMER reportsmounted clean.
Files
vcd_flush_poc.cβ decisive PoC (HAMMER route; static PID1 binary).vcd_trim_poc.cβ the FFS-TRIM route explored first (kept as the document of why it is dead: ffs_alloc.c:1673).odcat.c,guest_prep.sh,build.sh,run.shβ harness.run.log/fix_run.log/panic.txtβ baseline panic, patched run.build.log/fix_build.log,env.txtβ build output, guest env.fix.diffβ the validated fix (git-apply-able against vcd.c).
DF-5171 VERDICT β vcd FLUSH/FREEBLKS panic: REPRODUCED, fix validated
1. Reproduced? YES β panic: vcd: bad b_cmd 5 inside mount(2)
Guest: DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC #0 Thu Jul 2 2026),
cc 8.3, vm.vkernel_enable=1. A purpose-built VKERNEL64 (built from the
guest's /usr/src, which mirrors the audited tree) was booted as:
./vkernel64 -m 256M -r root.img -c hammer.img -e init_path=/poc
root.imgβ 128 MB UFS carrying the static PID1 PoC (vcd_flush_poc.c);hammer.imgβ 2 GBnewfs_hammer -fvolume, attached as the CD.
/poc runs mount("hammer", "/mnt", 0 /*rw*/, &info) with
info.volumes = {"/dev/vcd0"}. Decisive console sequence (run.log:34-46):
DF-5171(B) vcd FLUSH panic PoC (hammer) start MOUNTING hammer on /dev/vcd0 (rw)... VCDDBG: bio cmd=1 off=0 bcount=16384 dev=vcd0 <- hammer reads vol header HAMMER(vktest2) recovery check seqno=000fffff VCDDBG: bio cmd=2 off=0 bcount=16384 dev=vcd0 <- rw mount writes header VCDDBG: bio cmd=5 off=0 bcount=0 dev=vcd0 <- hammer_io_flush_sync() panic: vcd: bad b_cmd 5 <- vcd.c:165 Uptime: 3s
(The VCDDBG lines came from a temporary one-line kprintf in the guest's
vcd.c used during triage; it is not part of the finding or the fix and
records the fatal bio's identity: BUF_CMD_FLUSH, offset 0, length 0 β
exactly hammer_io.c:1995-1999.)
The mount(2) never returns; the vkernel dies at 3s uptime. Backtrace
and panic.txt in the pack.
2. The chain, line by line
hammerrw mount on/dev/vcd0β mount-time recovery walks the undo FIFO and then callshammer_io_wait_all(hmp, ..., 1)(hammer_recover.c:1438/1461/1465; every flusher cycle does the same, hammer_flusher.c:784/910).hammer_io_flush_sync()(hammer_io.c:1985-2007):getpbuf,bp->b_cmd = BUF_CMD_FLUSH,bio_offset = 0,b_bcount = 0,vn_strategy(volume->devvp, &bp->b_bio1).- devfs spec strategy passes non-READ/WRITE bios straight through
(
devfs_spec_strategy, devfs_vnops.c β theb_cmd != READ && != WRITEshortcut βdev_dstrategy_chain, kern_device.c:392). /dev/vcd0is a disk-core covering device (disk1_ops, subr_disk.c:716-745) βdiskstrategyβdscheck()(subr_diskslice.c:91): offset 0 / length 0 is sector-aligned and in bounds β passes through untouched (dscheck never filters by b_cmd).dev_dstrategy(dp->d_rawdev)βvcdstrategy()βswitch (bp->b_cmd)has no FLUSH case βpanic("vcd: bad b_cmd 5")(vcd.c:154-167).
3. Fix validated (baseline panic β patched clean)
fix.diff adds the vdisk-mirror FLUSH case (fsync(sc->fd),
cf. vdisk.c:332-337) and completes unknown commands instead of panicking.
Guest rebuilt with only that diff applied to vcd.c:
fix_run.log:35 HAMMER(vktest2) mounted clean, no recovery needed fix_run.log:39 mount(hammer) returned OK fix_run.log:40 RESULT: no-panic (STILL ALIVE after 15s) fix_run.log:58 Uptime: 18s
15 seconds of HAMMER flusher cycles (multiple FLUSH bios each completed via fsync) β no panic, mount fully functional.
4. Secondary discovery (killed route, real bug, out of this file's scope)
The FFS-TRIM producer was explored first and is dead code in this
tree: ffs_blkfree() gates the trim bio on
ip->i_devvp->v_mount->mnt_flag & MNT_TRIM (ffs_alloc.c:1670-1677) β
but i_devvp is the device vnode, whose v_mount is devfs, never
the UFS mount that carries MNT_TRIM. Empirically proven inside the
vkernel: statfs("/mnt") showed f_flags=01001040 (MNT_TRIM set), file
deletions and reallocation writes produced only cmd=1/cmd=2 bios
(instrumented run), and the bitmap frees went through ffs_blkfree_cg.
β FFS TRIM never issues trim bios anywhere in this kernel β worth a
separate finding against ffs_alloc.c (one-word fix:
ITOV(ip)->v_mount).
5. Reachability preconditions (honest scoping)
- Requires a vkernel booted with
-c <image>β which today crashes ininit_disk(DF-1031:diskFlags == NULLderef, init.c:487-489). The PoC applies a two-line guest-local enabler (NULL β static zero flags, plus O_RDWR for the CD backing to allow a rw mount β vcd's dev_ops include physwrite, so a writable image is an ordinary configuration). With DF-1031 fixed upstream, the panic is reachable on any vkernel given a CD image. - Trigger needs guest-root (mounting a filesystem). Guest-unprivileged users cannot mount; once root mounts HAMMER the panic is immediate.
- Impact ceiling: DoS of the vkernel process (userspace). No host kernel impact, no host privilege boundary crossed β hence Low, not Medium.
6. Verdict
reproduced / impact panic (of the vkernel target process) /
confidence certain. Fix: add FLUSH handling mirroring vdisk and stop
panicking on unknown commands β validated on the guest.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedSingle-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_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).
Confirmed kernel references
- 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
Detail
Exploit chain
vkernel booted with -c
Evidence (decisive lines)
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 mount's flags).
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 (init's stdio is dead in the vkernel) plus /marker. Harness mirrors DF-5010's: 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 guest's /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 mount's 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.
Verified 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.
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.
No comments yet.