DF-3067 / verdict.json
{ "finding_id": "DF-3067", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED on the stock INVARIANTS kernel (fresh vm.sh reset with-src): mounting a crafted HAMMER image whose undo FIFO is a 256KB ring of valid 8-byte PAD records (vol_version=6, first=next=UNDO|0x1000, alloc=UNDO|0x40000) makes hammer_recover_stage1's version-4 seqno backscan (hammer_recover.c:257-270) spin forever - PAD records are exempt from the seqno discontinuity check (:266-269), hammer_recover_scan_rev wraps at the zone base making the FIFO a ring (:789-790), and the loop has no byte counter or lap limit (the sibling fwd-scan bound is #if 0'd at :312-335). Observed: mount never returns; 47,039+ 'rev scan_offset' debug iterations captured while still running, first traced offset == crafted first_offset, ring wrap 3000000000000000->300000000003fff8 at iterations 513 and 33281, all 32,769 ring offsets revisited (lap>=2); the mounting thread burns 100% CPU in kernel mode and kill -9 pends forever (no cancellation points); vn0 stays wedged; guest otherwise alive. Read-only mount suffices; works on INVARIANTS and production kernels. Stage2 variant shares the root cause: its extended-range scan (:652-670) terminates only at ext_offset = the unvalidated REDO_SYNC.redo_offset u64 (:423-424). FIXED and validated: fix.diff bounds both backscan loops to one FIFO lap; on the rebuilt kernel #1 the identical image walks exactly one lap then fails the mount cleanly ('recovery failure during seqno backscan' -> 'Failed to recover HAMMER filesystem on mount'), mount process exits, guest up; stock-image mount/RW cycle and hard-crash dirty recovery regressions all pass.", "exploit_chain": "", "evidence": [ "bootlog_walk_trace.log: 47,039 'hammer_recover_scan_rev: rev scan_offset ...' console lines (still growing at capture), first offset 3000000000001000 == crafted first_offset", "run.log: walk wrap analysis - offset jumps UP 3000000000000000 -> 300000000003fff8 at iterations 513 and 33281 (hammer_recover.c:789-790 ring wrap); 32,769 distinct offsets = entire ring revisited; guest alive (fresh ssh answered PROBE_OK)", "fix_run.log (kernel #1 + fix.diff): one lap traced, then 'recovery failure during seqno backscan' + 'Failed to recover HAMMER filesystem on mount', mount_hammer exits (zombie->reaped), guest up", "fix_build.log: make nativekernel KERNCONF=X86_64_GENERIC completed; kernel #1 Sat Sep 5 22:10:54 UTC 2026", "padflood.c: the forger - PAD-8 pattern {0xC84E,0x8040,8} flood + vol0_blockmap[3] patch (no vol_crc fixup needed, DF-3042)" ], "kernel_refs": [ "sys/vfs/hammer/hammer_recover.c:250-270", "sys/vfs/hammer/hammer_recover.c:266-269", "sys/vfs/hammer/hammer_recover.c:312-335", "sys/vfs/hammer/hammer_recover.c:423-424", "sys/vfs/hammer/hammer_recover.c:652-670", "sys/vfs/hammer/hammer_recover.c:789-790", "sys/vfs/hammer/hammer_recover.c:901-930" ], "poc_changes": "Pass-2 author wrote the PoC from scratch (padflood.c) on the DF-3040 harness pattern (newfs_hammer base image + vol0_blockmap patch, no CRC fixup). Two iterations: (1) both packs initially shipped /root/run.sh causing an scp collision - the first guest run accidentally executed DF-3068's desync script (which reproduced DF-3068 immediately); renamed to run3067.sh/run3068.sh. (2) Shrank the PAD ring from the stock 512MB FIFO to 256KB (alloc=UNDO|0x40000) so the cyclic walk's wrap is visible quickly in the debug trace.", "attempts": 3, "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": 2100, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -O -o padflood padflood.c -I/usr/src/sys/vfs/hammer; truncate -s 12G + newfs_hammer -L uabase)", "run_cmd": "sh run3067.sh", "code_hash": "9dee8b15160f8fa2963263bf24c926c06bbf765cd9c89cc33cda337119e6f35f", "notes": "Dos/livelock class (not memcorrupt): the unbounded walk only reads FIFO buffers - no corruption, no escalation path identified. Unkillable spinning thread per mount attempt; vn device wedged until reboot. Same trust boundary as DF-0797/0798/0812/2594/3040 (mount of crafted image: root, or unprivileged with vfs.usermount=1). Guest was reset (with-src) after validation; guest_dirty=0 refers to the returned state.", "recommended_fix": "Bound the stage1 backscan and the stage2 extended-range scan to one FIFO lap (walked >= HAMMER_OFF_LONG_ENCODE(alloc_offset) -> EIO); see fix.diff", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sat Sep 5 22:10:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Identical PoC image on the patched kernel walks exactly one lap then fails the mount cleanly ('recovery failure during seqno backscan' -> 'Failed to recover HAMMER filesystem on mount'); mount process exits and is reaped; guest stays up. Stock-image regressions pass: mount/RW-create/sync/umount/remount cycle clean; hard-crash dirty recovery (reboot -q on mounted dirty fs) clean - 'recovery check seqno=0010014d', mount RC=0, all crash-surviving files intact.", "fix_evidence": "fix_run.log, fix_build.log (kernel #1 build), fix.diff" } |