DF-2623 / verdict.json
{ "finding_id": "DF-2623", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "The cited use-after-release READ is confirmed end-to-end; the claimed worse face (printing recycled buffer content) did NOT manifest, honestly recorded. Trace: hammer2_fixup_pfses runs on every first RW mount (vfsops.c:1336-1338); with the testvol PFS inode bref's PFSROOT bit cleared (forge_2623.py), the kprintf arm at :2404-2406 executes. ripdata is captured at :2399 pointing into the DIO buffer; hammer2_chain_modify COWs the inode (chain.c:1797 io_bread of the new block, :1827 bcopy, :1858 hammer2_io_bqrelse of the OLD dio, :1859 chain->data redirected), then the kprintf reads ripdata->filename through the released buffer. Instrumented kernel proof: 'DF2623: ripdata=0xfffff80051cc6800 vs chain->data=0xfffff80051cc7000 (same=0) name-by-old-ptr=\"testvol\" name-by-new-ptr=\"testvol\"' - the print source is no longer the chain's data. Content stayed intact on every attempt: bqrelse returns the buffer to the cache with data preserved, the COW landed in the same 64KB window on the observed run, and no I/O can land between :1858 and the kprintf within the same call chain - so no observable stale bytes, no panic, no userland disclosure (console message is privileged context anyway). Low severity as filed is right: a latent wrong-pointer read with a sub-microsecond recycling race as the only theoretical harm. fix.diff prints from chain->data->ipdata.filename (the COW copy, always valid); on kernel #2 the message prints identically and the fixup semantics (flag repair + flush, silent second mount) are unchanged.", "exploit_chain": "", "evidence": [ "run_stock.log - stock kernel: first RW mount prints 'hammer2: Correct mis-flagged PFS testvol'; directory lists; second mount silent (repair flushed)", "console_excerpts.txt [B] - instrumented kernel: DF2623: ripdata=0xfffff80051cc6800 vs chain->data=0xfffff80051cc7000 (same=0), names equal - read-through-released-pointer proven with content intact", "console_excerpts.txt [A]/[C] - stock and fix kernels both print the message once per freshly-forged image", "forge_2623.py output - PFS bref flags 0x1 -> 0x0 (PFSROOT cleared), sroot CHECK_NONE, volhdr CRCs recomputed" ], "kernel_refs": [ "sys/vfs/hammer2/hammer2_vfsops.c:2366-2418", "sys/vfs/hammer2/hammer2_vfsops.c:2399", "sys/vfs/hammer2/hammer2_vfsops.c:2401-2406", "sys/vfs/hammer2/hammer2_vfsops.c:1336-1338", "sys/vfs/hammer2/hammer2_chain.c:1797", "sys/vfs/hammer2/hammer2_chain.c:1827", "sys/vfs/hammer2/hammer2_chain.c:1858-1859" ], "poc_changes": "No seed code. Needed three passes at the image: the fixup REPAIRS the flag and flushes it to the vn-backed file on first RW mount, so the image must be re-forged from base before each kernel's run (documented in run.sh/VERDICT). An unrelated latent bug noticed while tracing (out of scope, not filed here): the 'chain->bref.type != INODE -> continue' at vfsops.c:2390-2391 does not advance the iterator - a non-INODE chain directly under the sroot would spin forever in this loop.", "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": 600, "guest_dirty": 0, "build_cmd": "sh build.sh (base image in guest; forge_2623.py on host)", "run_cmd": "vnconfig -c vn0 craft2623.img && mount -t hammer2 /dev/vn0@testvol /mnt/h2x && ls /mnt/h2x && umount /mnt/h2x && vnconfig -u vn0", "code_hash": "forge_2623.py sha256 da9e8ff8236ffd04d190ca4f17eac77aa5ea45590b48953956901ccb7db1d653; run_2623.sh sha256 1395eb124abbd6da903f4b1f725b47484a09a24f3cb03004bb0a183bd4ba84a7", "notes": "Classification rationale: the defect as filed ('kprintf prints ripdata->pfs_name from the released buffer') literally reproduces - the read provably comes from the released buffer; observable staleness does not. If the strict reading requires garbage output, this is 'inconclusive' for that face; the pointer-provenance proof plus the modify/release ordering (chain.c:1858-1859) makes the use-after-release itself certain.", "recommended_fix": "Print the name from chain->data->ipdata.filename (post-COW, always valid) instead of the pre-modify ripdata pointer in hammer2_fixup_pfses.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Sat Aug 29 00:17:00 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (single-line source change) included in kernel #2 (fix_build.log). Freshly re-forged image, RW mount: 'hammer2: Correct mis-flagged PFS testvol' prints once with the correct name from the post-COW chain->data; second mount silent; directory lists normally. The kprintf can no longer read through a released buffer because its argument is the chain's current data by construction.", "fix_evidence": [ "fix.diff", "console_excerpts.txt [C]", "fix_build.log / fix_install.log" ] } |