# DF-0771 — Missing data_len validation on HAMMER inode load

**Title:** Missing data_len validation on inode load — crafted image heap OOB
**Severity:** Medium
**Class:** heap OOB read / info leak via crafted filesystem image (mount-time parsing)

## Reproduce

### Deterministic harness (unprivileged, no kernel effects)
```
./build.sh && ./run.sh
```
Builds `df0771_harness` (userspace; copies the real kernel structs and the
verbatim CRC inline logic) and prints both halves of the bug:
- CRC gate bypassed for an INODE leaf with `data_len != sizeof(inode_data)`
  and `data_crc = 0` (`hammer_crc.h:267` returns 0, matches on-disk 0).
- Struct copy at `hammer_inode.c:525` reads 128 bytes unconditionally; with
  `xoff > 16256` it reads past the 16 KiB HAMMER data buffer.

### Live kernel path (needs root — threat model is admin mounting attacker media)
```
sudo ./reproducer-live.sh oob    # OOB variant -> panic on unpatched, EIO on patched
sudo ./reproducer-live.sh        # non-OOB CRC-bypass variant -> mounts on unpatched, EIO on patched
```
Creates a HAMMER image, corrupts the root-inode B-Tree leaf (`data_len=1,
data_crc=0`, bumps `data_offset` xoff), recomputes the B-Tree node CRC
using the real `iscsi_crc32` from `sys/libkern/icrc32.c`, then mounts.

## Expected results

| kernel | OOB variant (`oob`) | non-OOB variant |
|---|---|---|
| unpatched (`#0`) | **panic** `hammer_get_inode+0x400: movq 0x78(%rax),%rax` | mounts (CRC bypass); root inode loads |
| patched (`#1`) | `mount: Input/output error`; dmesg `bad inode data_len 1`; guest up | `mount: Input/output error`; guest up |

## Build / run commands (exact)
- build: `cc -O2 -Wall -o df0771_harness df0771_harness.c && cc -O2 -Wall -o corrupt corrupt.c`
- run (harness): `./df0771_harness`
- run (live, as root): `./reproducer-live.sh oob`

## Preconditions
- Harness: none (unprivileged userspace).
- Live path: root (vnconfig/newfs_hammer/mount_hammer). `vfs.usermount=0` on
  this guest; the realistic threat is an admin/operator mounting
  attacker-supplied removable media.

## Fix
See `fix.diff` (validated: single-fix kernel rejects the crafted image with
EIO; full before/after in `VERDICT.md`).
