# VERDICT — DF-3149

## Status: REPRODUCED (panic) / fix VALIDATED

## Baseline (stock INVARIANTS kernel `#0`, 2026-07-02)

Single `HAMMERIOC_MIRROR_WRITE` ioctl, two mrecs:
1. REC: `btype='R'`, create_tid=0x1000, delete_tid=0, data_len=0 → creates a
   well-formed record through the normal mirror path.
2. PASS: same base, `delete_tid=0x1000` (== create_tid).

Result: kernel panic (serial console):

```
panic: assertion "elm->leaf.base.delete_tid != elm->leaf.base.create_tid"
       failed in hammer_delete_at_cursor
       at /usr/src/sys/vfs/hammer/hammer_object.c:2502
hammer_delete_at_cursor() at hammer_delete_at_cursor+0x729
hammer_ioc_mirror_write() at hammer_ioc_mirror_write+0x860   (write_pass/update inlined)
hammer_ioctl() at hammer_ioctl+0xebe
hammer_vop_ioctl() at hammer_vop_ioctl+0x48
vop_ioctl() at vop_ioctl+0x63
```

(The KKASSERT spans hammer_object.c:2501-2502 in the audited tree; the guest
build reports :2502.)

Root cause chain (line-accurate):
- `hammer_mirror_check()` (hammer_mirror.c:777-786) approves an update whenever
  the wire delete_tid differs from the on-disk one and is nonzero — it never
  compares against create_tid.
- `hammer_mirror_update()` (hammer_mirror.c:814-833) only guards
  `delete_tid == 0`, then passes the wire tid straight to
  `hammer_delete_at_cursor(cursor, HAMMER_DELETE_ADJUST, mrec->leaf.base.delete_tid, ...)`
  (mirror.c:828-831).
- ADJUST stores it first (hammer_object.c:2467-2470), THEN asserts
  `delete_tid != create_tid` (hammer_object.c:2501-2502). Wire
  delete_tid == existing create_tid ⇒ guaranteed assertion failure.
- Reachable from both `hammer_ioc_mirror_write_rec` (mirror.c:625-626) and
  `hammer_ioc_mirror_write_pass` (mirror.c:696-697). NOTE: unlike DF-3148 this
  needs no crafted record creation — ANY existing slave record whose
  create_tid the master knows (mirror reads disclose create_tid) is killable
  with one PASS mrec.

Non-INVARIANTS production kernels: no panic; the record is stored with
`delete_tid == create_tid` — the exact duplicate-record B-Tree invariant
violation the code comment at hammer_object.c:2496-2502 warns about
(subsequent update chains can create duplicate keys).

## Fix validation (patched kernel `#1`, 2026-09-07, fix.diff applied)

`fix.diff` rejects `mrec->leaf.base.delete_tid <= cursor->leaf->base.create_tid`
in `hammer_mirror_update` (hammer_mirror.c:832-841 after patch).

Re-run of the identical PoC on a fresh fs:

```
=== duptid on patched kernel ===
MIRROR_WRITE rc=0 head.error=22 head.flags=00008000 count=80
=> kernel REJECTED the stream (error 22)
```

No panic; `count=80` proves mrec 1 was still created normally, and mrec 2 was
rejected with EINVAL exactly at the update path. Guest stayed up.
Build: `make -j6 nativekernel` RC=0, `make installkernel` RC=0.

## Bottom line

Certain. Single-ioctl kernel panic (INVARIANTS) / on-disk duplicate-record
state (production) from an unvalidated wire delete_tid; malicious-master
remote DoS + mirror-target B-Tree integrity corruption. Fix proven.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
