β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2288

OOB heap write/read in fw_bus_explore_callback via unchecked ongoaddr (Config ROM parser)

Summary

When host reads remote FireWire node Config ROM during bus enumeration fw_bus_explore_callback() writes each 4-byte response into fwdev->csrrom[(ongoaddr-CSRROMOFF)/4] BEFORE verifying ongoaddr in range. Sole bounds check at :1558 uses > instead of >= (off-by-one allowing csrrom[0x100]) and reached only AFTER write. Directory-entry jump at :1520 (fc->ongoaddr += csrreg->val*4) has NO bounds check so malicious ROM with Unit-Directory key (0x81/0xd1) and large 24-bit val lands ongoaddr past CSRROMSIZE. Next callback writes attacker-controlled bytes into heap after csrrom[] overwriting fwdev->rcnt fwdev->fc pointer fwdev->status fwdev->link. On 64-bit csrrom[0x101]/[0x102] alias low/high halves of fc pointer; corrupting yields controlled kernel-pointer deref in fwmem_xfer_req. rommax set to ongoaddr with no upper bound so FW_GCROM copyout leaks kernel heap to userspace. Attacker: any device physically on IEEE 1394 bus. No authentication required. Impact: kernel heap corruption with attacker-controlled value and offset.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2288 Β· 6 files
FileTypeDescriptionSize
VERDICT.md verdict gate analysis + source trace (write-before-check, off-by-one, unbounded dir jump) 4.8 KB ↓ raw
fix.diff suggested-fix bound ongoaddr before write, clamp directory-entry jump, fix > -> >= 1003 B view raw
build.sh build-script documents HW gate (no binary built) 454 B view raw
run.sh run-script prints gate proof 519 B view raw
env.txt environment guest uname + pciconf/ifconfig/usbconfig/kldstat/dmesg 1.1 KB view raw
gate_proof.txt gate-proof pciconf grep fwohci empty, /dev/fw* absent 61 B view raw
VERDICT.md verdict gate analysis + source trace (write-before-check, off-by-one, unbounded dir jump)
↓ download raw

DF-2288 β€” OOB heap write/read in fw_bus_explore_callback (CSR ROM parser)

Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)

Hardware gate (why the PoC cannot run on this guest)

The DragonFlyBSD audit guest is a QEMU/KVM VM with no FireWire (IEEE 1394) hardware whatsoever. Confirmed at audit time:

$ pciconf -l          # no fwohci / firewire bridge present
hostb0@pci0:0:0:0:   class=0x060000 ...  (i440fx hostbridge)
isab1@pci0:0:1:0:    class=0x060100 ...  (PIIX3 ISA bridge)
atapci0@pci0:0:1:1:  class=0x010180 ...  (PIIX3 IDE)
vgapci0@pci0:0:2:0:  class=0x030000 ...  (Bochs/QEMU std VGA)
virtio_pci0/1 ...                           (net + blk)
$ ls /dev/fw*         # /dev/fw*: No such file or directory
$ ifconfig -l         # vtnet0 lo0  (no fwip/fwe firewire net iface)

There is no FireWire controller (fwohci) attached, no /dev/fw* character device, and no firewire network interface. The vulnerable path fw_bus_explore_callback() runs only in the FireWire stack's bus-enumeration state machine, which is driven by fwohci interrupt delivery from real 1394 hardware during bus exploration. With no fwohci device and no /dev/fw* node reachable by the unprivileged maxx user, the callback can never be driven by attacker-controlled Config-ROM responses on this guest.

The hw.firewire.* sysctl nodes exist only because the firewire subsystem is compiled into the kernel; they are inert defaults β€” pciconf -l proves no controller is probed/attached.

Source trace β€” the bug is REAL (sys/bus/firewire/firewire.c)

The claim is accurate. The write happens BEFORE the bounds check, the bounds check is an off-by-one (> instead of >=), and the directory-entry jump advances ongoaddr with no bounds check at all.

fw_device carries u_int32_t csrrom[CSRROMSIZE/4] = csrrom[0x400/4] = csrrom[256] (sys/bus/firewire/firewirereg.h:51-54; CSRROMOFF = CSRROMSIZE = 0x400). Valid offsets are csrrom[0..255].

  1. Write-before-check. firewire.c:1500: c fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4] = ntohl(rfp->mode.rresq.data); stores the attacker-controlled (peer-supplied) 4-byte Config-ROM quadlet into csrrom[] indexed by ongoaddr unconditionally, then the only bounds check appears ~58 lines later at firewire.c:1558: c if((fc->ongoaddr - CSRROMOFF) > CSRROMSIZE){ // > should be >= goto nextnode; } So ongoaddr == CSRROMOFF + CSRROMSIZE (index 256, one past end) is written first and only then (incorrectly) allowed through.

  2. Unbounded directory-entry jump. firewire.c:1514-1524: when the parser sees a Unit-/Directory-entry key (0x81/0xd1) it does c fc->ongoaddr += csrreg->val * 4; // firewire.c:1520 csrreg->val is a 24-bit field taken directly from the (malicious) ROM with no upper bound. A crafted ROM can therefore advance ongoaddr far past CSRROMSIZE, and the next callback iteration writes attacker bytes at csrrom[(ongoaddr-CSRROMOFF)/4] deep past the end of the fw_device heap object. On amd64 csrrom[0x101]/[0x102] alias the low/high halves of the following fc pointer (firewirereg.h fw_device layout), yielding a controlled kernel-pointer dereference in fwmem_xfer_req.

  3. Info-leak path. rommax is set to ongoaddr with no upper bound (firewire.c:1501-1503), and the General-Config-ROM copyout (FW_GCROM) later copies csrrom[] out to userspace, leaking post-buffer kernel heap.

Impact (on real FireWire hardware, absent here): kernel heap write of attacker-controlled value at an attacker-influenced offset, plus a kernel-heap info-leak via FW_GCROM. Trigger: any device physically on the IEEE 1394 bus; no authentication.

Exploit chain status

Not pursuable on this guest β€” the primitive is gated behind absent FireWire hardware (valid Phase-6 hard blocker: "vulnerable code path dead/unreachable at runtime on this guest AND no harness can exercise it" because it requires a live 1394 peer responding with crafted ROM quadlets). The bug is a real, write-capable heap-corruption primitive on hardware that owns a 1394 controller; the defense-in-depth fix below closes it.

PoC changes

None. No PoC source was authored or runnable: the /dev/fw* device does not exist and the fwohci controller is absent, so neither a userspace ioctl reproducer (which would need an attached FireWire peer's responses) nor a kernel harness can drive fw_bus_explore_callback on this guest. The README claim was verified purely by source trace.

Bound ongoaddr before the write and fix the off-by-one. See fix.diff (supersedes finding proposal by hardening the directory-jump path the finding markdown focuses on, plus the >β†’>= correction).

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.

git apply --check findings/poc/DF-2288/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
↓ fix.diffn/a (no target HW/device on this guest)

Confirmed kernel references

Detail

Exploit chain

none β€” valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.

Evidence (decisive lines)

usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.

PoC changes

Created findings/poc/DF-2288/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).

Verified recommended fix

bound ongoaddr before write, clamp dir-jump, fix > to >=. Full git-apply-able diff in findings/poc/DF-2288/fix.diff (git apply --check OK).

Verdict

NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). firewire fw_bus_explore_callback OOB heap write/read; no FireWire controller, no /dev/fw, ifconfig vtnet0 lo0 only. Gate confirmed via usbconfig list (No device match / no /dev/ugen), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path. write-before-bounds-check at firewire.c:1500, off-by-one at :1558, unbounded dir-jump at :1520; malicious 1394 peer needed