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

OOB heap write in fw_bus_explore_callback via wire-controlled ongoaddr jump -> arbitrary 4-byte kernel heap write

Summary

fw_bus_explore_callback at firewire.c:1500 writes fc->ongodev->csrrom[(ongoaddr-CSRROMOFF)/4]=ntohl(rfp->mode.rresq.data) UNCONDITIONALLY before bounds check at :1558. At :1520 fc->ongoaddr += csrreg->val*4 where csrreg->val is 24-bit wire-controlled and ongoaddr is 16-bit bitfield (firewirereg.h:107-109) -> wraps. Malicious FireWire device: reply to RREQQ at offset 0x418 with key=0x81(CROM_UDIR) val=((TARGET-0x418)/4)mod 0x4000 -> ongoaddr wraps to TARGET -> next RREQQ response written at csrrom[(TARGET-0x400)/4] up to ~0x3BFF entries (~60KB) past csrrom[0] -> corrupts fwdev->fc (kernel pointer), fwdev->link.next (STAILQ), fwdev->status. No authentication needed. Chain with FW_GCROM info-leak -> KASLR defeat + arbitrary write -> kernel RIP control. Fix: bound (ongoaddr-CSRROMOFF)/4 < nitems(csrrom) BEFORE write; cap csrreg->val.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1020 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source source-level ongoaddr wrap + OOB write demo 4.3 KB view raw
build.sh build-script cc -o harness harness.c 86 B view raw
run.sh run-script ./harness 87 B view raw
build.log build-log harness build output 13 B view raw
run.log run-log harness output: wrapping + write-before-check 716 B view raw
env.txt environment guest env: no FireWire controller 365 B view raw
fix.diff suggested-fix move bounds check before the csrrom write 736 B view raw
fix_build.log fix-build-log combined-fix kernel build rc=0 191 B view raw
VERDICT.md verdict full source-level analysis 3.6 KB ↓ raw
README.md readme build/run instructions 733 B ↓ raw
live_reachability_check.txt reachability-test Live FireWire reachability evidence - no FW HW 1.0 KB view raw
README.md readme build/run instructions
↓ download raw

DF-1020 β€” fw_bus_explore_callback unconditional OOB csrrom write

Build

./build.sh

(or: cc -o harness harness.c)

Run

./run.sh

(or: ./harness)

Expected output

The harness demonstrates that ongoaddr (16-bit bitfield) wraps via csrreg->val*4 (24-bit device-controlled), and the write at firewire.c:1500 occurs BEFORE the bounds check at :1558.

Preconditions (for runtime trigger)

  • A FireWire controller present (to enable bus exploration).
  • A malicious FireWire device responding with crafted Config ROM.
  • This QEMU guest has NO FireWire hardware β€” source-level only.

Fix

fix.diff β€” move the bounds check (ongoaddr - CSRROMOFF) >= CSRROMSIZE to BEFORE the write at firewire.c:1500.

VERDICT.md verdict full source-level analysis
↓ download raw

DF-1020 β€” fw_bus_explore_callback unconditional OOB csrrom write β€” VERDICT

Verdict: INCONCLUSIVE (runtime) / CONFIRMED (source-level)

The bug is confirmed real by line-by-line source tracing. Runtime reproduction is blocked by missing FireWire hardware (no controller, no /dev/fw* on this guest).

Mechanism (source-level trace)

  1. Code location: fw_bus_explore_callback() in sys/bus/firewire/firewire.c:1494-1561 (the else branch for Config ROM exploration responses).

  2. Unconditional write before bounds check: - Line 1500 (WRITE): fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4] = ntohl(rfp->mode.rresq.data); - Line 1558 (CHECK β€” too late): if((fc->ongoaddr - CSRROMOFF) > CSRROMSIZE) goto nextnode;

  3. ongoaddr wrapping: At line 1513-1520: c csrreg = (struct csrreg *)&fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4]; if (csrreg->key == 0x81 || csrreg->key == 0xd1) { csrd->ongoaddr = fc->ongoaddr; fc->ongoaddr += csrreg->val * 4; // val is 24-bit device-controlled - csrreg->val (iec13213.h:129): u_int32_t val:24 β€” device-controlled. - fc->ongoaddr (firewirereg.h:107-109): u_int32_t ... ongoaddr:16 β€” a 16-bit bitfield that truncates on overflow. - val * 4 can be up to 0xFFFFFF * 4 = 0x3FFFFFC, wrapping the 16-bit ongoaddr to any value.

  4. OOB write: After wrapping, the next call to fw_bus_explore sends a new read request. The response hits line 1500, writing to csrrom[(wrapped_ongoaddr - CSRROMOFF)/4]. If ongoaddr < CSRROMOFF, the unsigned subtraction produces a massive index β†’ write far past csrrom[256].

  5. Target struct layout (struct fw_device, firewirereg.h:44-62): csrrom[256] (1024 bytes) | rcnt (4) | *fc (8, KERNEL POINTER) | status (4) | STAILQ link (8) | adjacent heap Overwriting *fc (kernel pointer to firewire_comm) gives RIP control when the corrupted pointer is later dereferenced.

Exploit chain

This is a device-attacker exploit: a malicious FireWire device on the bus responds to exploration reads with crafted Config ROM. The OOB write corrupts the fw_device struct (kernel pointer, linked list pointers, status). Combined with FW_GCROM info leak (KASLR defeat), this yields arbitrary kernel write β†’ potential RIP control.

Not a local user exploit β€” requires physical/bus access to a FireWire controller with a malicious device.

Why runtime reproduction is blocked

  • No FireWire controller on this QEMU guest.
  • /dev/fw* nodes don't exist.
  • The firewire driver is compiled into GENERIC but not active.
  • The bus exploration callback only fires when a FireWire controller initiates node discovery β€” impossible without HW.

PoC changes

Created harness.c β€” demonstrates ongoaddr 16-bit bitfield wrapping arithmetic and the order-of-operations bug (write before bounds check). Built and verified on the guest.

Fix validation

The fix (fix.diff) moves the bounds check BEFORE the write:

if ((fc->ongoaddr - CSRROMOFF) >= CSRROMSIZE)
    goto nextnode;
fc->ongodev->csrrom[...] = ntohl(rfp->mode.rresq.data);

Applied + compiled in combined-fix kernel (#1, rc=0, boots cleanly). Runtime before/after not possible (no FireWire HW).

fix_status: not_testable (diff applies + compiles; runtime blocked by missing HW).

Move the bounds check (ongoaddr - CSRROMOFF) >= CSRROMSIZE to BEFORE the write at line 1500 (currently the check is at line 1558, after the write). This matches the finding proposal. Additionally, consider capping csrreg->val at a sane maximum to prevent 16-bit wrapping of ongoaddr.

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

Detail

Exploit chain

none β€” HW-gated. Requires malicious FireWire device responding during bus exploration.

Evidence (decisive lines)

fw_bus_explore_callback in kernel: YES
FireWire controllers: 0 (pciconf)
OHCI controllers: 0
/dev/fw*: does not exist
QEMU: no FireWire emulation

PoC changes

Added live_reachability_check.txt confirming no FireWire HW.

Verified recommended fix

fix.diff moves bounds check before the write at firewire.c:1500. Matches finding proposal.

Verdict

INCONCLUSIVE (HW-gated, source-confirmed). fw_bus_explore_callback (0xffffffff804bcb20) IS compiled into the kernel. The bug (unconditional write to csrrom[] at firewire.c:1500 before bounds check at :1558, with 16-bit ongoaddr bitfield wrapping via 24-bit csrreg->val) is traced line-by-line and confirmed real via harness. BUT: fw_bus_explore_callback is ONLY called during FireWire bus exploration, which requires an fwohci OHCI controller. Live verification: 0 FireWire controllers in pciconf, 0 OHCI controllers (PCI class 0x0c0010), /dev/fw* does not exist. QEMU does not emulate FireWire.