# DF-5785 — unvalidated on-disk blockref type byte → chain_alloc panic (pass 3)

## Finding (short)

The type byte of every hammer2 blockref array element read from media is
attacker-controlled and never validated on the read path. Any lookup that
selects a slot with a type outside 1..6 (e.g. 0xAA) drives
`hammer2_chain_lookup` → `hammer2_chain_get` → `hammer2_chain_alloc`
(sys/vfs/hammer2/hammer2_chain.c:209-210) into a **raw `panic()`** —
present in production kernels, INVARIANTS not required. Deterministic:
mount the crafted image, read the directory once → panic.

## Files

* `craftA.img` — forged from `../DF-5784/base.img` (f1 DIRENT bref type →
  0xAA; ancestors methods=CHECK_NONE; volhdr CRC32Cs recomputed) by
  `../DF-5784/forge_df5784.py` variant A
* `run_df5785.sh` — guest runner (control + crafted)
* `panic.txt` — captured kernel panic (exact cited path)
* `run.log` / `run_fix.log` — baseline (panic) and patched (clean) runs
* `fix.diff` — validated fix (shared with DF-5784)

## Build / Run

See `../DF-5784/build.sh` (produces craftA.img) then on the guest:

```
sh /root/poc/df5784/run_df5785.sh
```

### Expected

* control `base.img`: mounts, `ls -l` shows `f1`, rc=0
* `craftA.img`: mounts, then `ls` → connection death; serial console shows
  `panic: hammer2_chain_alloc: unrecognized blockref type: 170` with the
  trace `chain_alloc ← chain_get ← chain_lookup ← xop_readdir`
  (panic.txt).
* on the patched kernel (#1, fix.diff): `ls` returns `total 0` cleanly,
  stat → ENOENT, umount OK.
