# DF-0769 — `hammer_vop_readdir` OOB read / info leak via forged direntry `data_len`

**Bug:** `sys/vfs/hammer/hammer_vnops.c:1728-1738` — `hammer_vop_readdir` trusts the
on-disk `int32_t data_len` from a B-tree direntry leaf. The only guard is a `KKASSERT`
(no-op on production/INVARIANTS-OFF kernels). A forged `data_len=8` underflows the name
length `data_len - HAMMER_ENTRY_NAME_OFF(=16)` to `-8`, narrowed to `uint16_t d_namlen =
65528`, driving a `bcopy(...,65528)` ~49 KB past the 16 KB `hammer_buffer` → OOB kernel-heap
read disclosed to userspace via `getdirentries`. On GENERIC (INVARIANTS ON) the KKASSERT
panics first (local DoS).

## How to reproduce (inside the DragonFly guest, as root)

```
cd /root/df0769
./build.sh                      # builds harness + craft_img
./harness                       # Part 1: deterministic ~49160-byte OOB read proof
./run.sh                        # Part 2: creates 1GB HAMMER image, forges it, mounts, ls /mnt
                                #   on GENERIC #0: PANIC at hammer_vnops.c:1728
                                #   on a FIXED kernel: clean EIO return, no panic
```
The full panic signature is in `panic.txt` (from `dfbsd-qemu/boot.log`).
The fix validation (before/after) is in `fix_run.log`.

## Files
- `craft_img.c` / `icrc32.c` — image forger (uses the kernel's own `iscsi_crc32`)
- `harness.c` — deterministic OOB-read proof (production/INVARIANTS-OFF ceiling)
- `fix.diff` — git-apply-able runtime bounds check (validated on a single-fix `#1` kernel)
- `VERDICT.md` — full narrative
- `build.log` / `run.log` / `fix_build.log` / `fix_run.log` / `panic.txt` / `env.txt`
