# DF-2605 — hammer2 `hammer2_chain_alloc` unvalidated radix → panic / DoS

Reproduction + fix-validation pack for DF-2605
(`sys/vfs/hammer2/hammer2_chain.c:189-190`). Same unvalidated-radix
root-cause class as the DF-2583 sibling, located at the upstream allocation
site.

## Reproduce

```sh
# in the guest, as the unprivileged user maxx (build only):
cd poc/DF-2605 && sh build.sh

# as root: create a base hammer2 image with an INDIRECT block
sh poc/DF-2605/setup_image.sh

# as root: forge radix=17 on the image, mount, readdir as maxx -> PANIC on #0
sh poc/DF-2605/run.sh 17
```

### Expected (bug present, unpatched `#0` GENERIC)
The `su maxx ./poc /mnt/h2test/testdir` step triggers
`panic: assertion "pbase != 0 && ((lbase + lsize - 1) & pmask) == pbase"
 failed in hammer2_io_alloc at /usr/src/sys/vfs/hammer2/hammer2_io.c:126`
— guest drops to `db>`, ssh dies. (radix=32 takes the UB path: gcc evaluates
`1U<<32`→1, so `bytes` is bogus; readdir returns `EDOM`, no panic, but the
chain is mis-sized — the UB the finding flags.)

### Expected (fixed `#1` kernel)
`getdents` returns `errno=33 (EDOM)`; **no panic**; guest stays up; dmesg
shows `hammer2_chain_alloc: ...: forged radix=17 (max=16); capping and
marking chain errored`. A clean (unforged) image still readdirs normally.

## Files

| file | purpose |
|---|---|
| `forge.c` | image forger: patches an INDIRECT bref's `data_off` radix and recomputes the full XXH64/CRC32C chain so the kernel accepts it |
| `crc32ctab.h` | CRC32C table (Castagnoli) for the volume-header icrc |
| `poc.c` | unprivileged trigger: `getdents` on the forged dir → INDIRECT chain load |
| `setup_image.sh` | root: builds a base 64 MB hammer2 image with a directory that spills into an INDIRECT block |
| `build.sh` / `run.sh` | exact build / run commands |
| `fix.diff` | git-apply-able fix: radix validation in `hammer2_chain_alloc` + early reject in `hammer2_chain_load_data` |
| `VERDICT.md` | full narrative (mechanism, impact, before/after, why-not-uid0) |
| `panic.txt` | baseline panic signature from the serial console |
| `run.log` / `run.2.log` | baseline run (radix=17 panic; radix=32 UB→EDOM) |
| `fix_build.log` | single-fix kernel build output (rc=0) |
| `fix_run.log` | patched-kernel run output (EDOM, no panic, no regression) |
| `env.txt` | guest uname / cc / sysctl / kernel sha256 |
| `manifest.json` | machine-readable artifact catalog |

## Build / run commands (exact)

- build: `cc -O2 -o forge forge.c && cc -O2 -o poc poc.c`
- run (as root, after `setup_image.sh`): `sh run.sh 17`
