{
  "finding_id": "DF-5497",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced on the QEMU guest with the production sys/bus/firewire/fwcrom.c compiled unmodified into a KLD harness (DF-5302/5321/5413 pattern; FireWire is hardware-gated in QEMU, so the harness feeds the production parser a device-controlled 1024-byte Config ROM exactly as fw_bus_explore() fills fwdev->csrrom[] and sbp(4) walks it). crom_next() derives the nested-directory pointer from the unvalidated 24-bit entry value (fwcrom.c:122, reg+reg->val, up to +64 MB forward) and loads ptr->dir->crc_len (fwcrom.c:130/134) BEFORE the CROM_END bound check on the next operand of the &&. Demonstrated: (1) userspace guard-page model -- the production parser faults at rom+1226 (200 bytes past the 1024-byte ROM, predicted window 1224..1227 for the crc_len bitfield load) and at rom+0x4000014 (full 24-bit reach), while the in-ROM control walks cleanly -- proving the load precedes the check that would have rejected the directory; (2) in-kernel, with the slab around the model ROM groomed with a crc_len=1 pattern, four crom_next: bound check failed oracle lines fired -- production reads of kernel heap memory outside the ROM that observably steer the walk; (3) a +4..+64 MB descent sweep panics the kernel: panic: assertion obj != NULL failed in vm_object_hold_shared, trap 000000000000000c, RIP dfh_crom_next+0x53 (the crc_len load), guest down (panic.txt). Read-only primitive (a wild directory can never be entered, so no OOB write from this defect); attacker is any device on the FireWire bus via sbp.c:412/468/567/602 crom_init_context/crom_search_key walks. Distinct from the six known findings (DF-1083/1084/1085/1086, DF-2289/2290/2291): those bound the damage to ~1 KB past the buffer or the root/text-leaf paths; this is the nested-directory dereference-before-validation with a 64 MB reach. Fix validated: with fix.diff (bounds-check the derived directory pointer before descending) applied to guest /usr/src, userspace subtests walk cleanly, oracle lines 4->0, the +4..64 MB sweep completes with no panic and the guest stays up.",
  "exploit_chain": "attacker: malicious/uncompromised FireWire peer device on the bus (unauthenticated, physical/prox access) -> bus reset: fw_bus_explore() fetches its Config ROM into fwdev->csrrom[256] (firewire.c:1500, firewirereg.h:52-54) -> sbp(4) probe walks it: crom_init_context + crom_search_key -> crom_next (sbp.c:412-425) -> ROM root directory contains a CSRTYPE_D entry (key 0xd1) with val=V -> kernel loads the 16-bit crc_len at csrrom+24+4*V without any prior bound -> mapped: silent foreign-memory read + 1-bit console oracle (bound check failed) per crafted V; unmapped: Fatal trap 12 in crom_next -> kernel panic (deterministic DoS). uid=0 chain not applicable: read-only primitive (wild directories are never entered), hardware-gated trigger.",
  "evidence": "panic.txt (serial console: trap 000000000000000c, RIP dfh_crom_next+0x53, four oracle lines before the fault); run.log (userspace guard-page model: predicted-address SIGSEGVs at rom+1226 and rom+0x4000014, clean control; KLD mode 1 groomed oracle 4x; mode 2 never returns); fix_run.log (patched: subtests clean, oracle 4->0, mode 2 survives, guest up); fix.diff; build.log/fix_build.log; VERDICT.md for the full path:line narrative",
  "kernel_refs": [
    "sys/bus/firewire/fwcrom.c:122",
    "sys/bus/firewire/fwcrom.c:130",
    "sys/bus/firewire/fwcrom.c:134",
    "sys/bus/firewire/iec13213.h:124",
    "sys/bus/firewire/iec13213.h:129",
    "sys/bus/firewire/firewirereg.h:52",
    "sys/bus/firewire/firewirereg.h:54",
    "sys/bus/firewire/firewire.c:1500",
    "sys/dev/disk/sbp/sbp.c:412",
    "sys/dev/disk/sbp/sbp.c:416"
  ],
  "poc_changes": "Harness written fresh for this file (DF-5302/DF-5321 KLD pattern: production fwcrom.c #included with -D-renamed globals; the only synthetic state is the device-controlled ROM buffer and its slab neighbourhood). Bring-up fixes: (1) re-added the fire-flag variable removed with a duplicate sysctl; (2) sys/queue.h include for the -DTEST userspace build; (3) userspace model fault prediction widened to a 4-byte window because GCC loads the crc_len bitfield as a halfword at dir+2; (4) mode-1 oracle made observable with a slab groom (64 same-class blocks patterned crc_len=1, every other freed, ROM allocated into a hole) after the first run read all-zero neighbours; (5) mode-2 probe 16 wraps to val=0 (16*0x100000 & 0xffffff), which descends into the ROM itself and produces one bound-check line against the known CROM_END slack (DF-2290) -- the +64MB probes are entries 0..15 steps 1..15.",
  "attempts": 4,
  "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": 1020,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh  (cd /root/df5497 && make -> df5497.ko with production fwcrom.c; cc -I/usr/src/sys -o c5497_user c5497_user.c /usr/src/sys/bus/firewire/fwcrom.c)",
  "run_cmd": "./c5497_user; kldload ./df5497.ko; sysctl kern.df5497_mode=1; sysctl kern.df5497_fire=1; sysctl kern.df5497_mode=2; sysctl kern.df5497_fire=1  (mode 2 expects the panic)",
  "code_hash": "9e9313b271891f73e76619b76c0ef9f0b71f54b007224c0e901e5687c2d7b238",
  "notes": "Baseline panic captured on the serial console after ssh died and vm.sh status => down (honest-status table: reproduced/panic). The remaining single bound check failed line in the PATCHED mode-2 run is the val-wrapped in-ROM descent walking against the pre-existing CROM_END unit slack tracked by DF-2290 (reads bounded ~1 KB past the ROM); the wild-pointer dereference this finding covers is fully eliminated. The walk-call counter (64) exceeds the 16 entries because in-ROM bogus directories with huge crc_len values are traversed entry-by-entry until the CROM_END check stops them; every such entry read stays inside the known slack window.",
  "recommended_fix": "In crom_next(), bounds-check the derived directory pointer ((vm_offset_t)dir <= CROM_END(cc)+1-sizeof(*dir)) before assigning ptr->dir and dereferencing crc_len; skip the entry as a leaf otherwise (fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "Baseline: userspace faults at the two predicted OOB addresses, 4 in-kernel oracle lines, mode-2 panic (guest down). Patched (fix.diff on /usr/src, module + model rebuilt, identical triggers): userspace subtests A/B walk cleanly, oracle lines 0, mode-2 sweep completes with no panic and the guest stays up.",
  "fix_evidence": "fix_run.log, fix_build.log, fix.diff, /tmp transcript of the patched userspace model (clean walks)"
}