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

ORB completion matching is address-only across ORB recycling (OCB_MATCH compares just orb_lo): a replayed management-ORB status completes the recycled *command* ORB β€” premature bus_dmamap_unload while device DMA is live and spurious CAM_REQ_CMP with device-chosen status

Summary

ORB slots have fixed bus addresses and no generation tag; OCB_MATCH compares only orb_lo. After a mgm ORB completes and its slot is recycled into a command ORB, a replay of the mgm status (device knows it; hostile peers can also write the status FIFO, physical-request DMA default-on per DF-1009) falls through to sbp_dequeue_ocb and matches the recycled command OCB by address: dmamap unloaded mid-DMA and CCB completed with device-controlled status while the real completion is dropped. fix.diff adds a one-deep stale_orb_lo filter.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3984 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.7 KB ↓ raw
VERDICT.md β€” 3.4 KB ↓ raw
fix.diff β€” 1.3 KB view raw
manifest.json β€” 1.1 KB view raw
verdict.json β€” 2.9 KB view raw

DF-3984 β€” sbp(4): ORB matching is address-only across ORB lifetimes β€” replayed mgm status completes a recycled command ORB (premature dmamap_unload under live device DMA + spurious CCB completion)

Where

sys/dev/disk/sbp/sbp.c β€” matching logic: - OCB_MATCH(o,s) = o->bus_addr == ntohl(s->orb_lo) (sbp.c:156) β€” the SBP status block carries only the low 32 bits of the ORB address; no source/sequence/generation discrimination beyond src. - mgm completion: sbp_recv1 sbp.c:1597-1603 matches target->mgm_ocb_cur by address, clears mgm_ocb_cur, later sbp_free_ocb(sdev, ocb) (sbp.c:1782) returns the OCB slot to free_ocbs β€” the slot keeps its bus_addr (fixed: sdev->dma.bus_addr + ... + sizeof(sbp_ocb)*i, sbp.c:519-522). - command completion: sbp_dequeue_ocb (sbp.c:2584-2615) matches any OCB on sdev->ocbs by the same address test, then unloads the data dmamap (sbp.c:2597-2598) and xpt_dones the CCB (sbp.c:1774).

The flaw

A hostile device (or hostile bus peer writing the initiator's status FIFO β€” physical-request writes are enabled by default per DF-1009) can replay a management-ORB status (same orb_lo) after the mgm OCB was completed and recycled into a command OCB. The replay: 1. misses mgm_ocb_cur (already NULL), 2. falls through to sbp_dequeue_ocb (sbp.c:1605) and matches the recycled command OCB by its inherited bus_addr, 3. bus_dmamap_unload (sbp.c:2597) while the device may still be DMA-transfering that command's buffer (pages can be freed/reused β†’ device writes land in unrelated memory), 4. completes the CCB with attacker-chosen status fields (sbp_status is fully device-controlled; resp=0,dead=0,len<=1,status=0 yields CAM_REQ_CMP) β€” the real completion later finds "No ocb on the queue" (sbp.c:1606-1610) and the mismatched data is never delivered.

Distinguishing from knowns

DF-2516 covers payload length validation (stale heap contents driving the same parse). This finding is the address-only lifetime flaw: even a perfectly received status block completes the wrong (recycled) ORB. No prior finding covers ORB-identity across recycling.

Severity

Medium: hostile-device-gated; premature dmamap_unload under live DMA is a memory-disclosure/corruption primitive; spurious completions corrupt I/O integrity. Source-certain; hardware-gated runtime (no FireWire on the audit guest β€” ../DF-3981/env.txt) β†’ inconclusive end-to-end.

Fix

fix.diff β€” per-LUN single-deep stale-address filter: when a management ORB completes, remember its bus_addr in sdev->stale_orb_lo; sbp_dequeue_ocb ignores (and reports) a matching stale address once, preventing the replay from ever matching the recycled command OCB.

VERDICT.md
↓ download raw

DF-3984 VERDICT β€” address-only ORB matching across ORB recycling

Status: inconclusive (hardware-gated; no FireWire controller on the audit guest β€” ../DF-3981/env.txt). Source-level certainty: likely (the state transitions are certain; the "replay" capability of a hostile device/peer is standard 1394 bus behavior β€” physical-request writes to the status FIFO are enabled by default per known DF-1009 β€” but the end-to-end corruption was not observable without hardware).

Analysis

ORB slots have fixed bus addresses for the LUN's lifetime: sdev->ocb[i].bus_addr = sdev->dma.bus_addr + SBP_LOGIN_SIZE + sizeof(struct sbp_ocb)*i + offsetof(orb) (sbp.c:516-522). Slot reuse moves an address between the mgm path and the command path with no generation tag; the SBP-2 status block's only ORB identity is orb_hi/orb_lo (ORB_MATCH compares only orb_lo, sbp.c:156).

Sequence a hostile device drives: 1. Initiator issues a management ORB (login/reconnect) from slot X (sbp_get_ocb sbp.c:1301, armed as target->mgm_ocb_cur sbp.c:1348). 2. Device answers correctly β†’ recv1 matches mgm_ocb_cur (sbp.c:1599), clears it (1601), completes the mgm ORB, sbp_free_ocb (sbp.c:1782) returns slot X to free_ocbs. 3. Local I/O allocates slot X for a command ORB (sbp_get_ocb sbp.c:2307, enqueued sbp.c:2638, dmamap loaded sbp.c:2346, ORB written to the device). 4. Device replays the step-2 status write (identical orb_lo; the initiator's status FIFO address is known to the device; a hostile third node can also write it β€” DF-1009). recv1: mgm_ocb_cur is NULL β†’ sbp_dequeue_ocb(sdev, status) (sbp.c:1605) matches the recycled command OCB in sdev->ocbs by address (sbp.c:2586). 5. Consequences: - bus_dmamap_sync/unload (sbp.c:2592-2598) while the device is still executing the command β†’ device DMA continues into pages the kernel considers unmapped (data may land in freed/reused pages); - CCB completed with CAM_REQ_CMP (device controls resp/dead/len/ status, sbp.c:1622-1770) while the data buffer content is stale; - the genuine completion is dropped ("No ocb on the queue", sbp.c:1606-1610); I/O integrity corrupted; - if the ORB had ORB_CMD_PTBL, ntohl(ocb->orb[4]) & 0xffff (sbp.c:2591) is evaluated on the current ORB β€” consistent β€” no extra primitive needed.

Note the sibling hazard inside one lifetime: a duplicate command status completes an in-flight ORB twice β€” the first xpt_done wins and the second finds nothing on the queue (benign); the recycling case above is the dangerous one because it completes an ORB that was NOT the one the status was generated for.

Fix validation

Not runnable on this guest. fix.diff adds a one-deep stale-address filter (sdev->stale_orb_lo) consumed by sbp_dequeue_ocb, so a replayed mgm status can never match the recycled command ORB. A protocol-complete fix would tag ORBs with a generation the device echoes β€” not available in the SBP-2 status format, hence the heuristic filter + loud report.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff applies cleanly; cannot be exercised without FireWire hardware.

fix.diff in pack
↓ fix.diffper-fix-DF-3984

Confirmed kernel references

Detail

Exploit chain

crafted SBP device: complete login on ORB slot X -> let host recycle slot X into a read command -> replay the login status (same orb_lo) -> host unloads the read's dmamap mid-DMA (device keeps writing into now-unmapped pages: disclosure into reused pages) and completes the read CAM_REQ_CMP with stale data -> repeat at will to corrupt I/O and race page reuse.

Evidence (decisive lines)

VERDICT.md (state-machine trace path:line); fix.diff (one-deep stale_orb_lo filter in sbp_dequeue_ocb/sbp_recv1 mgm match, patch --dry-run clean)

PoC changes

No runnable PoC (no FireWire hardware); state machine verified by source trace.

Verified recommended fix

Record the completed mgm ORB address per LUN and drop one matching stale status before it can match a recycled command ORB (fix.diff); a protocol-complete fix needs a generation tag the SBP-2 status cannot carry.

Verdict

ORB completion matching in sbp(4) is address-only (OCB_MATCH sbp.c:156 compares only orb_lo) and ORB slots are recycled between the management path and the command path with no generation tag (fixed slot addresses sbp.c:516-522, sbp_free_ocb sbp.c:1782). A hostile device (or hostile bus peer writing the initiator status FIFO - physical-request writes enabled by default per DF-1009) can replay a management-ORB status after its slot has been recycled into a live command ORB: recv1 misses the cleared mgm_ocb_cur, falls to sbp_dequeue_ocb (sbp.c:1605) and matches the recycled command OCB by address (sbp.c:2586), unloads its dmamap (sbp.c:2597) while the device may still be DMAing the command buffer, and completes the CCB with fully device-controlled status (sbp.c:1748-1774); the genuine completion is then dropped (sbp.c:1606-1610). Source transitions certain; the replay is standard 1394 bus capability; end-to-end untestable on this guest (no FireWire hardware) -> inconclusive, confidence likely.