DF-5498 / verdict.json
{ "finding_id": "DF-5498", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "Reproduced with the production ROM builder compiled for userland (fwcrom.cs 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 builders 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_chunks 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.", "exploit_chain": "", "evidence": "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", "kernel_refs": [ "sys/bus/firewire/fwcrom.c:442", "sys/bus/firewire/fwcrom.c:459", "sys/bus/firewire/fwcrom.c:517", "sys/bus/firewire/fwcrom.c:518", "sys/bus/firewire/fwcrom.c:520", "sys/bus/firewire/fwcrom.c:522", "sys/bus/firewire/iec13213.h:232", "sys/bus/firewire/iec13213.h:241", "sys/bus/firewire/firewire.c:624", "sys/bus/firewire/firewire.c:626" ], "poc_changes": "Model written fresh. Bring-up fixes: (1) #include <sys/queue.h> before iec13213.h (its -DTEST builder structs use STAILQ_*); (2) -Dmain=fwcrom_test_main applied only to the fwcrom.c translation unit (a global -D renamed the harness main too and duplicated the symbol); (3) offset arithmetic via offsetof(struct crom_chunk, data)+sizeof(data) so the pre-fill marks exactly the bytes past data[].", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 180, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -DTEST -Dmain=fwcrom_test_main -c /usr/src/sys/bus/firewire/fwcrom.c; cc -DTEST -c c5498_user.c; cc -o c5498_user ...)", "run_cmd": "sh run.sh (./c5498_user; exit 42 == REPRODUCED)", "code_hash": "dc0bd7e485173ae853c137728f581f97fffadb71bf2449e23e11cb476e60180e", "notes": "Info-grade by the mission rubric (hardening/correctness, no demonstrated confidentiality/integrity impact in current layouts: the over-read bytes are zeroed tail padding and no attacker-controlled string reaches the builder). Verified anyway because the userspace model was free in the same session; evidence class = production-code userspace model (fwdev/firewire precedent family). The spurious-quadlet/short-string stale-byte aspect affects EVERY text length, not just 69..72.", "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).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Baseline: 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_evidence": "fix_run.log, fix_build.log, fix.diff" } |