# DF-2291 — `crom_parse_text` bounds check missing `sizeof(u_int32_t)` → OOB read past ROM buffer

**Status: REPRODUCED (verbatim-code rig)** — impact class: kernel info leak (OOB read),
**independent of DF-2290** (triggers with the default `info_len = 4`).
**Fix: A/B-validated in the same rig** (`fix.diff` hunk 2).

## What this pack contains

Same layout as the DF-2290 pack (`fwcrom_rig.c`, `build.sh`, `run.sh`, full logs,
`fix.diff`, `env.txt`). The rig and build scripts are identical; `run.sh` drives the
`df2291_*` modes.

## How to reproduce

```sh
./build.sh
./run.sh
```

## Expected output (baseline = unmodified production code)

```
RIG mode=df2291_guard result=OOB_FAULT_CONFIRMED
  ... fault address is 0 bytes past the buffer end        # SIGSEGV reading textleaf->text[3] at byte 1024
RIG mode=df2291_canary result=OOB_LEAK_CONFIRMED
  crom_parse_text output (the buffer that becomes sdev->vendor/product, sbp.c:606,621):
    ab0de000 ab0de001 ab0de002 c0ffee00 c0ffee01 c0ffee02 c0ffee03 c0ffee04
  words 3..7 = canary 0xC0FFEE00..04 read from bytes 1024..1043, PAST the 1024-byte buffer
RIG mode=control result=PARSED_INBOUNDS                   # rig sanity
```

With `fwcrom_rig_fixed`: `NULLSTR_NO_FAULT (leaf rejected by bounds check: out="(null)")`
for both attack modes; `control` still `PARSED_INBOUNDS` (no regression).

## Attacker input (the crafted ROM)

Default bus-info block (`info_len = 4`), root directory at byte 20 with one entry
`{key=CROM_TEXTLEAF(0x81), val=244}` pointing at a text leaf placed at byte 1000 whose
`crc_len = 10` **quadlets**. The buggy check at `fwcrom.c:207` tests `1000 + 10 <= 1023`
(passes) but the read loop then consumes `text[0..7]` = bytes 1012..1043 — 20 bytes past
the 1024-byte buffer. The 5 canary words that land in the output string are the proof
that adjacent-kernel-heap contents flow into the buffer that becomes the SCSI INQUIRY
vendor/product string in production (`sbp.c:606,621`).
