DF-5785 / verdict.json
{ "finding_id": "DF-5785", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "The blockref type byte from media is unvalidated: any array slot with type outside 1..6 selected by hammer2_base_find()/combined_find reaches hammer2_chain_get -> hammer2_chain_alloc, whose switch panics (hammer2_chain.c:207-210) - a raw panic() active in production kernels. Reproduced on the stock INVARIANTS guest: crafted image (f1 DIRENT bref type=0xAA) mounts cleanly, the first directory read (ls) panics with hammer2_chain_alloc: unrecognized blockref type: 170 and the exact cited trace chain_alloc <- chain_get <- chain_lookup <- xop_readdir (panic.txt). Control image behaves normally. Fix (hammer2_bref_sane() type check + base_find skip, shared with DF-5784) validated on a rebuilt kernel: mount + ls + stat + umount all clean, no panic.", "exploit_chain": "", "evidence": "panic.txt (panic + full backtrace matching hammer2_chain.c:209 path); run.log (baseline: control lists f1, crafted ls kills the session); run_fix.log (patched kernel: SURVIVED_LS rc=0, DF5785_DONE)", "kernel_refs": [ "sys/vfs/hammer2/hammer2_chain.c:194-212", "sys/vfs/hammer2/hammer2_chain.c:2050-2098", "sys/vfs/hammer2/hammer2_chain.c:2606-2615", "sys/vfs/hammer2/hammer2_chain.c:4975-4986" ], "poc_changes": "Forger written fresh from the DF-2616 rig technique; type-byte patch needed no check-code recompute for the child bref itself, only methods=CHECK_NONE on ancestors + volhdr CRC32C recompute", "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 (baseline); fix kernel #1 Mon Oct 12 15:27:54 UTC 2026", "runtime_sec": 1200, "guest_dirty": 0, "build_cmd": "host: python3 forge_df5784.py base.img craftA.img craftB.img (variant A); guest kernel: config + make -j6 + make install in /usr/obj/usr/src/sys/X86_64_GENERIC", "run_cmd": "sh /root/poc/df5784/run_df5785.sh", "code_hash": "run_df5785.sh 50785e7c8fa028f5ff1b76d222e938f335963793a6e45ffd49ab50c9c1f181ff; forge_df5784.py 00929839f27a3e2a5edede64ca3f5980ec16e48fb37666d1cd32c68849bec2fa", "notes": "Pseudo-types 254/255 in a media array are also accepted by the alloc switch and take a second bad path (load_data panic at hammer2_chain.c:1086) - same missing-validation class, covered by the same fix. Fail-stop panic, no corruption primitive.", "recommended_fix": "Reject/skip media blockrefs with non-media type bytes at array-scan time (hammer2_bref_sane() + base_find skip); keep chain_allocs panic as defense-in-depth", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (shared with DF-5784) applied, kernel rebuilt (#1 Oct 12 15:27:54 2026), rebooted, exact PoC re-run: craftA mounts, ls returns total 0, stat ENOENT, umount clean (run_fix.log). Panic gone.", "fix_evidence": "run_fix.log; fix_build.log; env_fixkernel.txt" } |