# VERDICT — DF-2290 (verify mode)

**status: reproduced** · reproduced=1 · impact=leak · confidence=certain
rig: verbatim-code (guest has no FireWire hardware/KLD — see env.txt)

## Claim under test

`crom_init_context()` (sys/bus/firewire/fwcrom.c:62-94) advances the ROM pointer by
`1 + hdr->info_len` quadlets (fwcrom.c:84) without validating `info_len` against the
1024-byte buffer, then anchors `CROM_END(cc) = stack[0].dir + MAX_ROM - 1`
(fwcrom.c:60, MAX_ROM=1004) — an end computed assuming `info_len == 4`. With a hostile
device's `info_len = 254`, the root directory lands at buffer+1020 and CROM_END at
buffer+2023, i.e. ~999 bytes past the object, so every subsequent "bounds" comparison
(fwcrom.c:131, 134, 200, 207) is against an out-of-bounds address and is satisfied by
fully out-of-bounds reads.

## How it was verified

Verbatim rig (`fwcrom_rig.c`) links the guest's UNMODIFIED production parser
`/usr/src/sys/bus/firewire/fwcrom.c` — sha256
`b38ce9b52a249f594b4f11e0914eaa7fb6fbbccb0e174611d6a97f875f1466c5`, byte-identical to
the audited workspace copy — compiled userspace exactly as `usr.sbin/fwcontrol/Makefile`
does (`-I/usr/src/sys`, `!_KERNEL`). The 1024-byte "csrrom" object is placed at the end
of an mmap'd page; the following page is either PROT_NONE (guard) or canary-filled.

1. **df2290_guard** — ROM with `info_len=254`, root dir `crc_len=0x100`. First entry
   deref (`crom_search_key()` → `reg->key`, fwcrom.c:152-153 via crom_get fwcrom.c:102)
   faulted at `buffer_end+3` (the key byte of root entry[0], whose quadlet occupies
   [1024,1028) — entirely outside the object). Run 3/3 identical. → the OOB read is
   real, immediate, and unconditional once the hostile ROM is parsed.
2. **df2290_canary** — same ROM, canary page carrying fake immediate entries; the
   searched key (CSRKEY_VENDOR) exists ONLY past the buffer. `crom_search_key()` walked
   8 out-of-bounds entries (the fwcrom.c:130-131 check passed for every one of them,
   because CROM_END itself is OOB) and returned `reg` at buffer+1052 with
   `reg->val = 0x00C0FFEE` — parser results sourced from memory the buffer does not
   own. → in the kernel, `reg->key`/`reg->val` from adjacent heap flow into
   fw_device/sbp state (`sbp.c:412-421, 567-568, 602-621`).
3. **control** — well-formed in-bounds ROM parses correctly with the guard page
   untouched → the faults above are caused by the hostile ROM, not the rig.

Production context (source-cited, not executable on this guest): the ROM arrives from a
remote FireWire device during bus exploration and is stored host-endian in
`fw_device.csrrom[1024/4]` (firewirereg.h:52-54; firewire.c:1496-1500), inside the
kmalloc'd `struct fw_device` (firewire.c:1311). OOB reads therefore run into adjacent
kernel heap; values/strings reach userland via sbp's `sdev->vendor`/`sdev->product`
filled by `crom_parse_text()` (sbp.c:606,621) and exposed through SCSI INQUIRY on the
sbp LUN. Read-only bug → leak/DoS ceiling, not corruption: CVSS AV:P (physical
FireWire attach or hostile device on the bus) as filed. Medium/kernleak stands.

## Fix A/B (same rig, `fix.diff` hunk 1)

Baseline reproduces (above). With hunk 1 applied (reject `info_len > 4` — the bus-info
block cannot exceed 4 quadlets in a 1024-byte CSR ROM, making CROM_END math sound):
`crom_init_context: info_len 254 out of range`, depth=-1, **zero** OOB access in both
attack modes; `control` unchanged (PARSED_INBOUNDS). No regression.
`info_len < 4` remains accepted and merely makes CROM_END conservative — safe.

## Honesty notes

- Userspace rig: proves the production parser's reads exceed the 1024-byte object
  (guard SIGSEGV) and that adjacent-memory contents are returned in parser results
  (canary). The kernel-side path (kmalloc adjacency, INQUIRY exposure) is source-cited
  (firewire.c:1311,1500; sbp.c:602-621), not executed — no FireWire hardware exists on
  the audit guest (env.txt), which is why the verbatim-rig evidence class was used.
- Incidental observation (NOT part of this finding, for the orchestrator): with a
  *valid* info_len, `crom_next()` still sets `ptr->dir = reg + reg->val` for
  directory-type entries without any range check (fwcrom.c:122) and dereferences
  `ptr->dir->crc_len` (fwcrom.c:130) BEFORE the CROM_END comparison — a directory entry
  with a large 24-bit `val` yields an OOB read up to ~64MB past the buffer even after
  DF-2290's fix. Recommend a follow-up finding.
- `crom_desc()` (userspace-only path, fwcontrol) inherits the same neutralized bounds.

## Bottom line

Confirmed as filed: unvalidated `info_len` moves the parser's entire bound past the
buffer, enabling OOB heap reads up to ~999 bytes past `fw_device.csrrom` with contents
flowing into user-visible SCSI strings. Fix is two lines of validation and is validated
A/B in this rig.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
