crom_add_simple_text() includes the 12-byte csrtext header in crc_len: crom_load()/crom_crc() read one quadlet past chunk->data[] (sub-object OOB read for 69..72-char text) and publish a spurious trailing quadlet + IEEE-1212-incorrect length in every broadcast text leaf
Summary
crc_len must count quadlets AFTER the crc quadlet (the directory builder's semantic, re-added as 1+crc_len at load) but the text builder includes the 3 header quadlets - double-counting one: every locally-built text leaf is broadcast with a spurious trailing quadlet (stale data.buf bytes) and a spec-off length; 69..72-char text reads one quadlet past chunk->data (KASAN-visible sub-object over-read whose bytes land in the host's ROM). Userspace model of the production builder: 0xCAFEBABE planted past data[] emerges verbatim in the generated ROM (29 vs 28 quadlets); count-after-crc fix validated (spec-correct, pattern gone).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5498 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 3.7 KB | β raw | |
| verdict.json | β | 4.5 KB | view raw | |
| c5498_user.c | β | 3.7 KB | view raw | |
| build.sh | β | 131 B | view raw | |
| run.sh | β | 107 B | view raw | |
| build.log | β | 195 B | view raw | |
| fix_build.log | β | 0 B | β download | |
| run.log | β | 797 B | view raw | |
| fix_run.log | β | 798 B | view raw | |
| env.txt | β | 280 B | view raw | |
| fix.diff | β | 395 B | view raw |
DF-5498 β crom_add_simple_text() crc_len includes the header quadlet; crom_load()/crom_crc() read one quadlet past chunk->data[] and publish it in the Config ROM
File: sys/bus/firewire/fwcrom.c β pass 2. Userspace-model verification
(fwcrom.c's own non-_KERNEL build path, as fwcontrol(8) links it).
Build / run (guest, any user with /usr/src readable)
sh build.sh # cc -DTEST (pulls in the builders) -Dmain=fwcrom_test_main sh run.sh # exit 42 == REPRODUCED
Expected (baseline)
case1 len=72: builder crc_len=21 (data holds 21 quads; 1+crc_len=22 quads copied/CRC'd) case1: PATTERN FOUND in generated ROM (OOB quadlet published) case2 len=68 (control): crc_len=20, PATTERN absent RESULT: REPRODUCED ...
The 4 bytes past chunk->data[] (tail of struct crom_chunk) are read by
crom_crc() (fwcrom.c:517-518) and crom_copy() (fwcrom.c:520-522) and copied
verbatim into the host's generated Config ROM. Also visible: the published
leaf crc_len is off by one against IEEE 1212 at EVERY text length (21 vs 20
for len=72; 20 vs 19 for len=68) β every text leaf this host broadcasts carries
a spurious trailing quadlet and a spec-incorrect length.
Fix validation
fix.diff (tl->crc_len = howmany(len, 4) + 2) applied to guest /usr/src,
model rebuilt: crc_len=20 == IEEE-1212-correct, PATTERN absent, ROM one
quadlet shorter per leaf (fix_run.log).
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 correct2 + howmany(len,4)(spec/type, lang_id, text β excluding the crc quadlet).
Consequences, for every text length:
crom_load()copies1 + crc_lenquadlets β exactly one quadlet past the chunk's real content β andcrom_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 ofchunk->data.buf[]) and a spec-incorrectcrc_len(off by one against IEEE 1212).- For text length 69..72 (
MAX_TEXT == 72,fwcrom.c:442),1 + crc_lenreaches 22 quadlets whilechunk->dataholds 21 (iec13213.h:232-241): a sub-object OOB read of 4 bytes pastdata[]β insidestruct crom_chunk's tail padding today (in-object, zeroed by every current caller'sM_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.
Fix verification
fixedBaseline: pattern quadread past chunk->data[] published in the ROM (exit 42), leaf crc_len off by one at every length. Patched (fix.diff on /usr/src, model rebuilt): crc_len == IEEE-1212-correct (20/19), pattern absent in both cases, ROM one quadlet shorter per text leaf.
fix_run.log, fix_build.log, fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (baseline: crc_len=21/20 vs correct 20/19, PATTERN FOUND in generated ROM, exit 42; control absent), fix_run.log (patched: crc_len==spec-correct, PATTERN absent both cases), build.log/fix_build.log, c5498_user.c, fix.diff
PoC changes
Model written fresh. Bring-up fixes: (1) #include
Verified recommended fix
tl->crc_len = howmany(len, sizeof(u_int32_t)) + 2; in crom_add_simple_text (fwcrom.c:459) so crc_len counts only the quadlets after the crc quadlet (fix.diff).
Verdict
Reproduced with the production ROM builder compiled for userland (fwcrom.c's own non-_KERNEL/-DTEST path, as fwcontrol(8) links it). crom_add_simple_text() sets tl->crc_len = howmany(sizeof(struct csrtext)+len, 4) = 3+ceil(len/4) (fwcrom.c:459), counting the 12-byte header although crc_len must count only the quadlets after the crc quadlet (the directory builder's semantic, fwcrom.c:394-407) and crom_load() adds the crc quadlet back (fwcrom.c:520-522). crom_load()/crom_crc() therefore always consume one quadlet past the text: every locally-built text leaf is published with a spurious trailing quadlet (stale bytes of chunk->data.buf[]) and a crc_len off by one against IEEE 1212 (demonstrated: leaf header crc_len field=21 where 20 is correct, and 20 where 19 is correct); for text lengths 69..72 (MAX_TEXT=72, fwcrom.c:442) the read reaches one quadlet past chunk->data[] -- a sub-object OOB read of 4 bytes (inside struct crom_chunk's tail padding; KASAN-visible, latent out-of-object read). Model: bytes past data[] pre-filled with 0xCAFEBABE -> after production crom_add_simple_text(72 chars)+crom_load(), the pattern is present verbatim in the generated ROM (29 vs 28 quadlets); 68-char control: exact fit, pattern absent. Impact Info: current kernel callers pass static strings / the root-settable hostname (firewire.c:624-626, dcons_crom.c:117-119), the over-read bytes are zeroed padding in every current layout (no leak), so this is correctness/interop (spec-wrong text leaves broadcast to bus peers) plus a latent hardening defect. Fix validated: with fix.diff (crc_len = howmany(len,4)+2) applied to guest /usr/src, crc_len=20 == spec-correct, pattern absent, ROM one quadlet shorter per leaf.
No comments yet.