# DF-5498 VERDICT — REPRODUCED (mechanism certain; Info-grade impact in current layouts)

## Root cause

The directory builder counts quadlets *after* the crc quadlet
(`crom_add_quad()` bumps `crc_len` per entry, `fwcrom.c:394-407`), and
`crom_load()` adds the crc quadlet back when emitting
(`fwcrom.c:520-522`, `len = 1 + chunk->data.crc_len`). The **text-leaf**
builder violates that semantic by including the whole 12-byte
`struct csrtext` header in `crc_len`:

* `sys/bus/firewire/fwcrom.c:459` —
  `tl->crc_len = howmany(sizeof(struct csrtext) + len, sizeof(u_int32_t));`
  = `3 + howmany(len,4)` instead of the correct `2 + howmany(len,4)`
  (spec/type, lang_id, text — excluding the crc quadlet).

Consequences, for **every** text length:

1. `crom_load()` copies `1 + crc_len` quadlets — exactly one quadlet **past
   the chunk's real content** — and `crom_crc()` (`fwcrom.c:517-518`) CRCs the
   same extra quadlet, so every locally-built text leaf is broadcast with a
   spurious trailing quadlet (stale/uninitialized bytes of `chunk->data.buf[]`)
   and a spec-incorrect `crc_len` (off by one against IEEE 1212).
2. For text length 69..72 (`MAX_TEXT == 72`, `fwcrom.c:442`), `1 + crc_len`
   reaches 22 quadlets while `chunk->data` holds 21 (`iec13213.h:232-241`):
   a sub-object OOB read of 4 bytes past `data[]` — inside `struct
   crom_chunk`'s tail padding today (in-object, zeroed by every current
   caller's `M_ZERO`/bzero), but a latent out-of-object read if the chunk
   layout/allocation ever changes, and a KASAN-visible defect.

Kernel callers pass static strings today (`firewire.c:624` "DragonFly
Project", `firewire.c:626` the root-settable **hostname**, `dcons_crom.c:117/
119` "FreeBSD"/"dcons"); only a 69..72-char hostname reaches the sub-object
OOB, so there is no attacker-controlled trigger — hence Info severity
(hardening/correctness), not Low+.

## Reproduction (userspace model, production code)

`c5498_user.c` + the unmodified production `fwcrom.c` (compiled via its
non-_KERNEL/-DTEST path): a `struct crom_chunk` whose bytes past `data[]`
(end of data at offset 116 of the struct) are pre-filled with `0xCAFEBABE`,
72-char text via production `crom_add_simple_text()`, then production
`crom_load()`:

```
case1 len=72: builder crc_len=21 ... PATTERN FOUND in generated ROM
               (published leaf crc_len field=21, IEEE-1212-correct=20)
case2 len=68 (control): crc_len=20, PATTERN absent   [exact fit]
RESULT: REPRODUCED: the quadlet past chunk->data[] was read by
        crom_load/crom_crc and published in the Config ROM   (exit 42)
```

The ROM quadlet stream is 29 vs 28 quadlets (len=72) — the extra one is the
pattern, i.e. the bytes past `data[]` verbatim.

## Fix validation

`fix.diff` (`tl->crc_len = howmany(len, 4) + 2`) applied inside the guest
`/usr/src`, model rebuilt: crc_len=20 == spec-correct for len=72, PATTERN
absent, control likewise correct, ROM one quadlet shorter per leaf
(`fix_run.log`).

## Impact

Info: no confidentiality/integrity impact in current kernel layouts (the
over-read bytes are the zeroed tail padding of a zero-initialized object);
correctness/interop impact (spec-incorrect text-leaf lengths + spurious
quadlet in every broadcast text leaf — visible to any bus peer that parses
per IEEE 1212) and a latent OOB read + KASAN defect.

## Artifacts

`run.log`, `fix_run.log`, `build.log`, `fix_build.log`, `c5498_user.c`,
`build.sh`, `run.sh`, `fix.diff`, `env.txt`.

## 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.
