# DF-2616 — OOB read/write past the 64KB DIO buffer from crafted hammer2 `data_off` geometry

**Finding:** DF-2616 (High, hammer2 bucket) — `hammer2_chain_alloc`/
`hammer2_chain_load_data` derive `chain->bytes` from the radix of the
attacker-supplied `bref.data_off` and never validate the *geometry*: the
offset's alignment to its radix or containment within one 64KB DIO window.
The only guard is a `KKASSERT` in `hammer2_io_alloc` (`hammer2_io.c:126`),
compiled out without INVARIANTS. A crafted image with a **perfectly valid
radix** but a misaligned / window-crossing offset makes `chain->data +
chain->bytes` overrun the kernel DIO buffer.

## What is in this pack

| file | what it is |
|---|---|
| `mkbase.sh` | guest-side script: builds a clean 64MB hammer2 image with f1 + N marker files |
| `forge_E.py` | host-side forger: walks volhdr→sroot→PFS→file-inode blocksets/indirects, relocates each `w` file's 1KB data block into its own 64KB window (valid geometry), crosses f1's DATA `data_off` (`lbase\|0xFF00`, radix kept), sets `methods=0x00` (CHECK_NONE) + `modify_tid` (in-place path), CHECK_NONEs all ancestor brefs, recomputes the 3 volhdr CRC32Cs |
| `forge_df2616.py` | earlier forger for variants A/B/C (small direct-blockset fs) |
| `h2_A_sroot_cross.img` | variant A: `volhdr.sroot_blockset[0].data_off = 0x180fd0a` (radix 10 **valid**, misaligned+crossing) — mount-time trigger |
| `h2_B_data_cross.img` | variant B: file f1's DATA bref crossed (`0x1c0ff0a`), CHECK_NONE |
| `h2_E_groom10.img` | variant E: f1 crossed (`0x200ff0a`, radix 10) + 32 marker files relocated one-per-window (grooming) |
| `h2_E_groom.img` | variant E with f1 = 64KB file (radix-16 crossed, OOB reach 0xFF00) |
| `trigger_leak.sh`, `trigger_write.sh`, `trigger_D.sh`, `trigger_E.sh` | guest-side trigger sequences |
| `panic_A.txt` | **mount-time panic on stock INVARIANTS kernel** (`hammer2_io.c:126`) |
| `panic_B.txt` | **panic loading the crafted FILE data chain** on stock kernel |
| `fault_B_noinv.txt` | **kernel page fault in `memmove` (OOB bcopy source) on the no-INVARIANTS kernel** |
| `run_E_noinv.log` | groomed run on no-INVARIANTS kernel: crossed read survives (mapped neighbor), full sequence |
| `forensic_E.txt` | post-run image forensics: attacker pattern planted through the crossed pointer at 0x200ff05 |
| `h2_E_flushed.img` | the flushed crafted image (evidence) |
| `build_noinv.log` | full build log of the no-INVARIANTS kernel #1 |
| `fix.diff` | the verified fix (geometry validation in load_data/modify/dedup + io.c log guard) |
| `build_fix.log` / `fix_run_*.log` | fix kernel build + before/after validation runs |
| `verdict.json` / `manifest.json` / `VERDICT.md` | machine + human verdicts |

## Reproduce

1. Base image: run `mkbase.sh` on the guest (root), pull `base.img` to the host.
2. Forge: `python3 forge_E.py base.img h2_E_groom.img` (needs python3 on host).
3. Push back, then on the guest as root: `sh trigger_E.sh`.
4. **Stock INVARIANTS kernel:** mounting variant A (or reading f1 on B/E)
   panics at `hammer2_io.c:126` — see `panic_A.txt` / `panic_B.txt`.
5. **no-INVARIANTS kernel** (`options INVARIANTS` commented out,
   `make -j6 nativekernel KERNCONF=X86_64_GENERIC`): the KKASSERT is gone;
   the crossed access either page-faults in `memmove` (ungroomed:
   `fault_B_noinv.txt`) or, with the 32-marker grooming, reads/writes the
   kernel buffer in the adjacent KVA slot (`run_E_noinv.log` +
   `forensic_E.txt`).
6. Fix: `git apply fix.diff`, rebuild, reboot — mount of A fails cleanly,
   reading f1 on E fails cleanly with `illegal data_off geometry` (see
   `fix_run_*.log`), no panic, no fault.

## Expected observable results

- INVARIANTS kernel: `Illegal: ...` + `panic: assertion "pbase != 0 && ..."` at `hammer2_io_alloc` — deterministic.
- no-INVARIANTS + ungroomed: `Fatal trap 12: page fault while in kernel mode ... memmove+0x28` (DDB stop).
- no-INVARIANTS + groomed: no fault; `read(2)` of f1 returns bytes from beyond
  the DIO buffer; `write(2)` plants attacker content through the crossed
  pointer (on-media proof at image offset `0x200ff05`).
- Fixed kernel: clean `EINVAL`/`EIO` failures, `illegal data_off geometry` console message.
