⬢ DragonFlyBSD Kernel Audit
DF-3152 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3152",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "Single privileged HAMMERIOC_ADD_VOLUME ioctl with vol_size claiming 2 GiB on a 2 MiB vn device panics the stock INVARIANTS kernel deterministically (twice, identical signature): hammer_format_volume_header derives vol_buf_end from the unchecked user value, hammer_install_volume never compares it against the devices real size, and hammer_format_freemaps layer2 breads walk past the device end -> panic: cluster_callback: unexpected EOF. On non-INVARIANTS kernels the same failure is worse silently: KKASSERTs compile out and hammer_update_volumes_headers unconditional return 0 overwrites the error, so the ioctl returns SUCCESS with a half-formatted volume installed and vol_count bumped on every member. Privileged (SYSCAP_NOVFS_IOCTL) trigger; also reachable as operator bad luck via transient device I/O error mid-add. Distinct from DF-0869 (integer overflow of the beg/end arithmetic); this is the missing device-size cross-check plus broken error-path discipline.",
 "exploit_chain": "n/a - privileged kernel panic/DoS + silent filesystem corruption primitive; no unprivileged route (ioctl double-gated on SYSCAP_NOVFS_IOCTL at hammer_ioctl.c:72,197)",
 "evidence": [
  "run.log:45-55 Initialize freemap volume 1 -> panic: cluster_callback: unexpected EOF with cluster_callback/vnstrategy stack, guest at db>",
  "run.2.log identical signature from a clean vm.sh reset (deterministic)",
  "VERDICT.md line-accurate trace incl. the production-kernel error-overwrite analysis (hammer_volume.c:117-130, hammer_volume.c:673-739)",
  "build.log full compiler output"
 ],
 "kernel_refs": [
  "sys/vfs/hammer/hammer_volume.c:662",
  "sys/vfs/hammer/hammer_volume.c:117-130",
  "sys/vfs/hammer/hammer_volume.c:415-476",
  "sys/vfs/hammer/hammer_volume.c:673-739",
  "sys/vfs/hammer/hammer_ondisk.c:109-272",
  "sys/vfs/hammer/hammer_ondisk.c:212",
  "sys/vfs/hammer/hammer_ondisk.c:709",
  "sys/vfs/hammer/hammer_ioctl.c:193-201"
 ],
 "poc_changes": "Seed sketch did not exist; authored from scratch. Notes: struct hammer_ioc_volume layout taken from /usr/src/vfs/hammer/hammer_ioctl.h in-guest (device_name is char[MAXPATHLEN], vol_size int64); include path is <vfs/hammer/hammer_ioctl.h> with -I/usr/src; ioctl issued on an O_RDONLY fd of the HAMMER1 mountpoint root.",
 "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": 30,
 "guest_dirty": 0,
 "build_cmd": "cc -O -I/usr/src -o voladd_lie voladd_lie.c",
 "run_cmd": "./voladd_lie /mnt/h1 /dev/vn1 2147483648  (vn0=12G sparse HAMMER1 fs rw at /mnt/h1, vn1=2MB)",
 "code_hash": "38ab12f7cc3d8c6a193ba1ad664d925e525b08a2f2e78cce6598036bdf29f212",
 "notes": "Panic manifests in cluster_callback (clustered read past EOF) rather than the predicted hammer_volume.c:118 KKASSERT - same root cause (unvalidated vol_size), the EOF-residual path fires first. Production-kernel variant (KKASSERT compiled out + error overwritten by hammer_update_volumes_headers unconditional 0 -> ioctl returns 0 with half-formatted volume) reasoned from source (hammer_volume.c:117-130 vs 673-739), not executable on the INVARIANTS guest. Also deletes cleanly: volume-del of a silently half-added volume later trips KKASSERT at hammer_volume.c:541/232.",
 "recommended_fix": "Validate the formatted vol_buf_end against the devices real size via DIOCGPART in hammer_install_volume (mirror HAMMER2 hammer2_ondisk.c:283-291), and replace the add paths KKASSERT/error-overwrite with real unwinding that uninstalls the volume and returns the error",
 "fix_status": "not_testable",
 "fix_kernel_uname": "",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "fix.diff authored and verified to apply cleanly (patch -p1 --dry-run) but kernel rebuild validation not performed: Low-severity privileged DoS, not a reproduced memory-corruption finding (mandatory fix-build class)",
 "fix_evidence": [
  "fix.diff"
 ]
}