DF-3000 / verdict.json
{ "finding_id": "DF-3000", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain ownership violation, empirically unreachable in the test window. hammer_vop_write's UIO_NOCOPY branch (sys/vfs/hammer/hammer_vnops.c:682-696) does bqrelse(bp) (unlocks + free-queues the getblk'd buffer) and then bread(&bp); DragonFly's breadnx (vfs_bio.c:908-911) reuses a non-NULL *bpp instead of getblk()ing a fresh locked buffer, so hammer proceeds to uiomovebp/bwrite/bawrite/bdwrite (hammer_vnops.c:824-844) on an UNLOCKED, free-queued buffer - every one of those sinks begins with an always-on panic('buffer is not busy') check, so a single firing = guaranteed kernel panic, plus a real recycle race window (another CPU's getnewbuf can reassign the buffer between bqrelse and breadnx's vn_strategy). Six scenarios (sparse/fill dirty patterns, 256MB-1.2GB buffer churn, 3GB anon pressure, concurrent children, MADV_DONTNEED, 8 rounds) on an instrumented kernel (kprintf probe verified in kernel.stripped) produced ZERO branch firings: hammer's fault path pre-populates each 16K block as B_CACHE with wired pages, so the branch requires the conjunction of buffer-recycle AND clean-sibling page-reclaim AND a still-dirty page, which did not materialize on a 4GB guest with swap. Filed as a latent defect: the same pattern sits in sys/vfs/hammer2/hammer2_vnops.c:1103-1108.", "exploit_chain": "", "evidence": "README.md (source chain with vfs_bio.c line cites) | run.log (all six scenario outputs; dmesg DF3000-PROBE count = 0) | probe.diff (exact instrumented-kernel patch) | VERDICT.md (reachability analysis + one-line fix)", "kernel_refs": [ "sys/vfs/hammer/hammer_vnops.c:682", "sys/vfs/hammer/hammer_vnops.c:690", "sys/vfs/hammer/hammer_vnops.c:824", "sys/kern/vfs_bio.c:908", "sys/kern/vfs_bio.c:986", "sys/vfs/hammer2/hammer2_vnops.c:1103" ], "poc_changes": "wrote five scenario programs (op3000.c..op3000e.c) attacking the !B_CACHE precondition from different angles (fill/sparse dirty, churn, madvise, anon pressure, concurrent buffer-demand children); plus a non-semantic kernel probe to observe branch reachability", "attempts": 6, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2: Sat Sep 5 01:44:36 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 120, "guest_dirty": 0, "build_cmd": "cc -O -o /tmp/op3000X /tmp/op3000X.c ; (probe kernel) make -j4 nativekernel KERNCONF=X86_64_GENERIC", "run_cmd": "run_all.sh (scenarios A-E) as unprivileged user on HAMMER1 /mnt/h1; then: dmesg | grep -c DF3000-PROBE", "code_hash": "826a89377bff2962688e5ed7631d543d63ac05d6d1b359de8a4b181772c3af78", "notes": "not a false positive: the contract violation is provable from vfs_bio.c semantics (bqrelse tail = BUF_UNLOCK; breadnx *bpp reuse; bdwrite/bawrite/bwrite always-on busy panics). The blocker is the B_CACHE shielding described in VERDICT.md. HAMMER2 carries the identical pattern (hammer2_vnops.c:1106) - recommend fixing both.", "recommended_fix": "Delete the bqrelse(bp) before bread() in hammer_vnops.c:693 (bread/breadnx with a pre-acquired locked *bpp already issues the read on the caller's buffer); same deletion in hammer2_vnops.c:1106." } |