{
 "finding_id": "DF-2296",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "leak",
 "confidence": "certain",
 "verdict": "pay_len = MAX(0, req.len - hdr_len) (fwdev.c:530) is never bounded by sizeof(asyreq->data)=2048; the bcopy at fwdev.c:560-561 reads pay_len bytes from the 2084-byte M_IOCTLOPS staging buffer (sys_generic.c:675). Proven on the stock binaries: req.len=4096 executes a 4080-byte copy (2032 bytes past the buffer); on S400 (fc->maxrec=10 per fwohci.c:521, gate=2048 - the original claim said 512, corrected) fw_asyreq rejects with send.pay_len > maxrec AFTER the bcopy already ran; on S800 (maxrec=11) the identical request completes the full path and the interposed TX start records a 4080-byte wire payload (the over-read bytes would be transmitted to the peer on hardware). The response-path variant (fwdev.c:582-584, executed with an emulated 2304-byte peer response) writes req.len-16 bytes into the staging buffer, 240 bytes past its end (canary corruption detected; chunk-level attribution inconclusive due to slab placement). SIBLING BUG DISCOVERED at the same line: the destination is (void *)&xfer->send.payload - the address of the pointer field - smashing the fw_xfer tail and overrunning the 160-class allocation with fully user-controlled bytes (three stock panics captured in _kfree/pmap_kvtom; behavioral control of smashed fields demonstrated). That sibling is a privesc-class local heap overwrite and is flagged for a new DF-ID. Fix for DF-2296 validated: pay_len bound returns EINVAL for 4096 with no transmit; legit 2064/2048 runs the full stock path identically.",
 "exploit_chain": "operator-group FW_ASYREQ ioctl with req.len>2064: bcopy over-reads up to ~63KB past the ioctl staging buffer into the TX payload (transmitted to a FireWire peer on maxrec>=11 hardware; rejected after the copy on S400). Locally the bytes are not readable back on stock; the sibling &payload destination bug turns the same ioctl into a local heap OVERWRITE (privesc-class, flagged separately).",
 "evidence": "run_2296.log (phase A dmesg send.pay_len > maxrec, phase B AT-START wire 4080, phase C SPRAYCHK corrupt=1), dmesg_phaseA_gate.txt, dmesg_serial_run.txt, smash_panic_gfp_kfree.txt (stock GPF _kfree <- fw_xfer_free_buf <- fw_ioctl), spraydump.*.txt, run.legit_baseline.log + fix_run.log (A/B: 4096 EINVAL no-tx, 2064 full path identical)",
 "kernel_refs": [
  "sys/bus/firewire/fwdev.c:521",
  "sys/bus/firewire/fwdev.c:530",
  "sys/bus/firewire/fwdev.c:560",
  "sys/bus/firewire/fwdev.c:561",
  "sys/bus/firewire/fwdev.c:565",
  "sys/bus/firewire/fwdev.c:582",
  "sys/bus/firewire/fwdev.c:583",
  "sys/bus/firewire/fwdev.c:584",
  "sys/bus/firewire/firewire.c:210",
  "sys/bus/firewire/fwohci.c:521",
  "sys/bus/firewire/firewire.h:73",
  "sys/bus/firewire/firewire.h:265",
  "sys/kern/sys_generic.c:675"
 ],
 "poc_changes": "Substantial: seed sketched a plain ioctl; the stock kernel proved un-runnable without handling the sibling &payload smash (three panics), so the PoC plants valid values into the fw_xfer fields the smash overwrites (offsets taken from rig-published kernel offsetof sysctls) to walk the full stock path; phases A/B/C added; rresb.len byte order fixed (high 16 bits).",
 "attempts": 7,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #8: Sun Oct 18 07:38:56 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/FWH  x86_64",
 "runtime_sec": 5400,
 "guest_dirty": 0,
 "build_cmd": "cd findings/poc/DF-2296 && sh build.sh",
 "run_cmd": "sh run_2296.sh (guest, phases A/B/C) then sh run.sh for fixed-kernel A/B",
 "code_hash": "e32f1ad19e6139fc9085be40f2801e138b6f351d824e88d0f67189432cca88d8",
 "notes": "Verified in VERIFY mode via the verbatim-interposition kernel harness (fwharness.c): stock fwdev.o/fwdma.o/firewire.o binaries, fabricated controller externalities, /dev/fw0.0 created against the real firewire_ops with stock 0660 root:operator clone perms. Guest has no FireWire hardware and stock GENERIC ships firewire without fwohci, so no /dev/fw node exists without hardware or a kld - reachability quantified per finding. Fix A/B on the same harness kernel: bug inputs return clean errors, legit inputs bit-identical/unchanged. Guest reset to clean-source snapshot after the run.",
 "recommended_fix": "Bound pay_len by sizeof(asyreq->data) -> EINVAL before fw_xfer_alloc_buf. Sibling (new finding): change (void *)&xfer->send.payload to (void *)xfer->send.payload at fwdev.c:561.",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #8: Sun Oct 18 07:38:56 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/FWH  x86_64"
}