# DF-3068 VERDICT — REPRODUCED (panic / dos)

**Status: reproduced / impact: panic / confidence: certain.** Baseline
verified on the stock INVARIANTS kernel (DragonFly 6.5-DEVELOPMENT #0);
fix validated on the rebuilt kernel #1.

## What was run

`padflood desync` patches the same stock `newfs_hammer` image to
`vol_version=3` (version<4 mounts take `first_offset`/`next_offset`
verbatim from `vol0_blockmap[3]`, hammer_recover.c:225-226) and writes a
24-byte CRC-valid DUMMY record at FIFO offset 0xFF0 whose tail bytes
(`{0xC74F, 0x0041, 24}` at 0x1000) double as the reverse walk's next fake
tail; `first=UNDO|0x1000`, `next=UNDO|0x1008` → nominal undo range of
8 bytes.

## Why it panics (path:line)

* `hammer_check_tail_signature` (`:1023-1026`) anchors the head at
  `end_off - tail->tail_size` — the **tail's** size — while
  `hammer_recover_scan_rev` (`:815`) steps the walk back by the **head's**
  `hdr_size`. For PAD/DUMMY-style short records `_hammer_check_signature`
  never requires the *previous* record's tail to agree with the head it
  points at (`:901-924` skips the agreement block; `:948-961` re-derives
  the tail from the head), so a crafted tail/size pair whose head is a
  24-byte record makes one loop iteration consume 24 bytes when only 8
  remain (`bytes -= head->head.hdr_size` at `:431`).
* The undo loop exits with `bytes = 8 - 24 = -16` and
  `KKASSERT(error || bytes == 0)` at `:460` fires → deterministic panic.

## Baseline observation (stock kernel #0, clean run after fresh reset)

```
HAMMER(uabase) recovery undo  3000000000001000-3000000000001008 (8 bytes)(RW)
panic: assertion "error || bytes == 0" failed in hammer_recover_stage1 at /usr/src/sys/vfs/hammer/hammer_recover.c:460
hammer_recover_stage1() at hammer_recover_stage1+0x2cb
hammer_vfs_mount() at hammer_vfs_mount+0xa3f
sys_mount() at sys_mount+0x36b
Debugger("panic")  -> guest wedged at db>
```
(reproduced twice; the first, accidental run hit the identical panic — see
panic_console_full.txt vs panic.txt.)

## Non-INVARIANTS behavior (production kernels)

The assert is compiled out: the loop exits silently with `bytes < 0`, the
mount **succeeds**, stage1 has executed a record from *outside* the
nominal undo range (the fake-tail DUMMY), and it rewrites + flushes the
volume-header blockmap (`:481-488`) — silent mis-recovery of FIFO indices.
No memory unsafety (all `hammer_recover_undo` copies are clamped at
`:1053-1076`), hence severity Low for this finding.

## Fix validation (fix.diff, kernel #1)

Size guard added (`hdr_size > bytes → EIO`) before record execution.
Identical PoC on the patched kernel:
`HAMMER(uabase) Corrupt UNDO record size 0018 at 3000000000000ff0` →
`mount: Input/output error`, **no panic**, guest up. Regressions (stock
mount/RW cycle, hard-crash dirty recovery) all clean.
