{
  "finding_id": "DF-2290",
  "status": "reproduced",
  "verdict": "REPRODUCED via verbatim-code rig on the QEMU guest (no FireWire hardware/KLD exists there, so production-handler dispatch is impossible; evidence class chosen accordingly). The rig links the UNMODIFIED production parser /usr/src/sys/bus/firewire/fwcrom.c (sha256 b38ce9b5..., byte-identical to the audited tree) compiled userspace exactly as usr.sbin/fwcontrol builds it, and drives it with a hostile devices config ROM against a 1024-byte object placed at a page boundary. info_len=254 moves the root directory to buffer+1020 and CROM_END to buffer+2023 (~999 bytes past the object): the first root-entry deref faulted on a PROT_NONE guard page at buffer_end+3 (quadlet [1024,1028) fully OOB), and with a canary page crom_search_key() walked 8 out-of-bounds entries -- the fwcrom.c:130-131 check passed every time because CROM_END itself is OOB -- returning reg->val=0x00C0FFEE planted only past the buffer. In production the same reads run into the kmallocd struct fw_device (firewire.c:1311) past csrrom[1024/4] (firewirereg.h:52-54, filled host-endian at firewire.c:1500), and the OOB-sourced values/strings reach userland via sbp.c:602-621 (sdev->vendor/product) into SCSI INQUIRY (sbp.c:1527-1528). Read-only primitive: leak ceiling, consistent with the filed Medium/kernleak classification. fix.diff hunk 1 (reject info_len>4) A/B-validated in the same rig: attack modes rejected with zero OOB access, well-formed ROM unregressed.",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "exploit_chain": "hostile FireWire device -> config ROM quadlets stored host-endian into fw_device.csrrom[1024/4] during bus exploration (firewire.c:1496-1500) -> crom_init_context advances root dir by unvalidated info_len=254 (fwcrom.c:84), CROM_END lands ~999B past the buffer (fwcrom.c:60) -> crom_search_key/crom_get dereference root entries at buffer+1024.. (fwcrom.c:102,152) with every bounds check neutralized (fwcrom.c:131) -> OOB heap values become reg->key/reg->val consumed by sbp target construction (sbp.c:412-421,567-568,602-621) and crom_parse_text copies OOB bytes into sdev->vendor/product -> local user reads them via SCSI INQUIRY on the sbp LUN (sbp.c:1527-1528). Rig proves the parser-level primitive + content flow; kernel adjacency and INQUIRY legs are source-cited (not executable without FireWire hardware).",
  "evidence": "run.log (all 3 runs identical): RIG mode=df2290_guard result=OOB_FAULT_CONFIRMED ... fault address is 3 bytes past the buffer end and RIG mode=df2290_canary result=OOB_LEAK_CONFIRMED ... crom_search_key(0x03) returned reg=...(buffer+1052; 28 bytes PAST...) reg->val=0xc0ffee; PATCHED cells: crom_init_context: info_len 254 out of range ... REJECTED_NO_FAULT; control cells PARSED_INBOUNDS on both binaries. build.log shows the verbatim sha256 and clean gcc 8.3 build of the unmodified production file.",
  "kernel_refs": [
    "sys/bus/firewire/fwcrom.c:60",
    "sys/bus/firewire/fwcrom.c:84",
    "sys/bus/firewire/fwcrom.c:102",
    "sys/bus/firewire/fwcrom.c:130-131",
    "sys/bus/firewire/iec13213.h:133-147",
    "sys/bus/firewire/firewirereg.h:52-54",
    "sys/bus/firewire/firewire.c:1311",
    "sys/bus/firewire/firewire.c:1496-1500",
    "sys/dev/disk/sbp/sbp.c:412",
    "sys/dev/disk/sbp/sbp.c:602-621"
  ],
  "poc_changes": "No seed pack existed (poc_path was empty); the rig was authored from scratch in verify mode. Iterations: (1) added <stddef.h> for ptrdiff_t under gcc 8.3; (2) regenerated fix.diff mechanically with diff -u after the guests GNU patch 2.0 rejected the initially hand-counted hunks; (3) NULLSTR classification moved so the fixed binarys (null) outcome is reported as the expected fixed behavior. Trigger values match the finding exactly (info_len=254, root crc_len=0x100).",
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "cd findings/poc/DF-2290 && ./build.sh   # cc -O0 -g -Wall -I/usr/src/sys -o fwcrom_rig fwcrom_rig.c /usr/src/sys/bus/firewire/fwcrom.c ; plus fwcrom_rig_fixed from fix.diff-patched local copy",
  "run_cmd": "cd findings/poc/DF-2290 && ./run.sh   # df2290_guard df2290_canary control x {baseline,patched}",
  "code_hash": "dc18340a00b31765d8256f263fd8273ebc87616b152fcfb07f8b72e86bf726c5",
  "notes": "Leak-class finding (CWE-125 OOB read): no uid0 escalation path -- primitive is read-only; guard-page SIGSEGV in the rig stands for read of memory the object does not own, in-kernel the same reads return adjacent kmalloc heap. 3 identical runs (run.log, run.2.log, run.3.log). Incidental observation for the orchestrator (NOT this finding): even with info_len<=4, crom_next() sets ptr->dir = reg + reg->val for directory entries without any range check (fwcrom.c:122) and dereferences ptr->dir->crc_len (fwcrom.c:130) BEFORE the CROM_END comparison -> OOB read up to ~64MB past the buffer via a large 24-bit val; recommend a follow-up finding. Guest left up and clean (only /tmp/fwverify used, removed).",
  "recommended_fix": "crom_init_context: reject hdr->info_len > 4 (bus-info block cannot exceed 4 quadlets in a 1024-byte CSR ROM) before computing the root-directory pointer -- see fix.diff hunk 1.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "A/B in the same verbatim rig: baseline reproduces both the guard-page fault and the canary leak; with fix.diff hunk 1 applied, crom_init_context rejects info_len=254 (depth=-1) and BOTH attack modes make zero out-of-bounds accesses, while the in-bounds control ROM still parses identically (no regression). Kernel rebuild not meaningful for this target: the parser is not reachable on the guest (no FireWire hardware/KLD) and the rig compiles the identical production source byte-for-byte.",
  "fix_evidence": "run.log PATCHED section: crom_init_context: info_len 254 out of range + RIG mode=df2290_guard result=REJECTED_NO_FAULT + RIG mode=df2290_canary result=REJECTED_NO_FAULT + RIG mode=control result=PARSED_INBOUNDS; build.log: Hunk #1 succeeded at 77."
}