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

dptexecuteccb send-failure path frees CCB without callout_stop β€” armed timeout later runs dpttimeout on stale/recycled state (premature abort of unrelated command / UAF read)

Summary

callout_reset arms the CCB timer before dpt_send_eata_command; its failure path unloads, frees and xpt_dones WITHOUT callout_stop (the normal completion path stops it). When the stale callout fires, dpttimeout reads the stale ccb and, if the dccb was re-dispatched, passes the ACTIVE check and EATA_SPECIFIC_ABORTs the new healthy command while stamping CAM_CMD_TIMEOUT. One-line callout_stop fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4243 Β· 6 files
FileTypeDescriptionSize
README.md β€” 982 B ↓ raw
VERDICT.md β€” 1.2 KB ↓ raw
env.txt β€” 1.0 KB view raw
fix.diff β€” 493 B view raw
verdict.json β€” 1.9 KB view raw
manifest.json β€” 614 B view raw

DF-4243 β€” send-failure path in dptexecuteccb leaves the timeout callout armed

dptexecuteccb arms the CCB timeout (callout_reset, dpt_scsi.c:778) before handing the command to the controller. If dpt_send_eata_command() then fails (HBA stays HA_ABUSY for ~1s: wedged/faulty controller), the error path at dpt_scsi.c:780-788 unloads the map, frees the dccb and xpt_done()s the ccb β€” but never callout_stop()s the armed timer (compare the normal completion path, which does: dpt_scsi.c:1572).

When the stale callout later fires, dpttimeout() runs on a freed/possibly recycled state: * dccb->ccb is stale β†’ xpt_print_path(ccb->ccb_h.path) reads a possibly recycled CAM ccb (dpt_scsi.c:1683-1685); * if the dccb was already re-allocated to a new command, the DCCB_ACTIVE check passes and dpttimeout sends EATA_SPECIFIC_ABORT (dpt_scsi.c:1707) against the new, healthy command and stamps CAM_CMD_TIMEOUT on its ccb β€” premature abort of unrelated I/O.

VERDICT.md
↓ download raw

DF-4243 verdict β€” UNTESTED (hardware-gated); callout leak certain from source

Reproduced?

No β€” triggering requires a DPT controller wedged in HA_ABUSY long enough to fail dpt_send_eata_command (dpt_scsi.c:1111-1119), then the (tens of seconds) SCSI timeout to fire on the stale callout. No hardware in guest.

Certainty

The asymmetry is provable: callout_reset at dpt_scsi.c:778 vs no callout_stop in the 780-788 failure path, while the completion path stops it at 1572. After xpt_done() the CAM ccb (and its embedded timeout_ch) is owned by the periph again β€” a later dpttimeout() on it is a use-after-free read (xpt_print_path) at minimum, and a premature abort of whatever the recycled dccb is now doing at worst. Severity Low: requires a wedged HBA to arm, and the damage is mostly availability.

Fix validation

Not possible without hardware; fix.diff adds the one-line callout_stop.

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 authored against the read-only sys/ tree; kernel-level fix validation impossible without DPT SmartRAID hardware (none in guest, no QEMU emulation exists).

fix.diff
↓ fix.diffper-fix-DF-4243

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md; dpt_scsi.c:778-788 vs 1572; dpttimeout 1676-1711

PoC changes

No seed PoC existed; evidence authored from scratch in this run.

Verified recommended fix

callout_stop(ccb->ccb_h.timeout_ch) in the send-failure path before dptfreeccb/xpt_done (fix.diff).

Verdict

Untested on guest (hardware-gated). Source-certain: dpt_scsi.c:778 arms the timeout, the failure path 780-788 frees dccb+ccb without callout_stop (the normal path stops it at 1572). Later dpttimeout() runs on stale pointers (1683-1707): UAF read via xpt_print_path or premature EATA_SPECIFIC_ABORT of a re-dispatched command.