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

XPT_RESET_DEV sends its FIB under a stale Header.Command (assigned only in the XPT_SCSI_IO case; aac_release_command() never clears it)

Summary

Header.Command is set only for XPT_SCSI_IO; the RESET_DEV case submits the recycled FIB with whatever opcode it last carried (release_command re-inits everything but Command) - resets are dispatched under an unrelated opcode (worst case a container ioctl) exactly during error recovery. Hw-gated; trace-certain. Set-command-on-common-path fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4717 Β· 4 files
FileTypeDescriptionSize
README.md β€” 1.9 KB ↓ raw
fix.diff β€” 780 B view raw
verdict.json β€” 1.6 KB view raw
manifest.json β€” 520 B view raw

DF-4717 β€” aac_cam XPT_RESET_DEV sends the FIB under a stale Header.Command

Root cause (code trace; hw-gated β€” no Adaptec adapter in the QEMU guest, runtime verification not possible)

fib->Header.Command is assigned ONLY in the XPT_SCSI_IO case of aac_cam_action() (sys/dev/raid/aac/aac_cam.c:452-453). The XPT_RESET_DEV case (:488-496) falls through to the common SRB setup (:501-521) and sends the FIB without ever setting the command opcode.

aac_release_command()'s "re-initialize" (sys/dev/raid/aac/aac.c:1395-1407) clears XferState, StructType, Flags, SenderSize, ReceiverFibAddress, SenderData β€” not Header.Command. Every FIB therefore carries the opcode of whatever previously used it (an earlier ScsiPortCommand, but equally a ContainerCommand, ClusterCommand, AIF or ioctl FIB routed through the same command pool).

Result: a device reset issued by CAM (XPT_RESET_DEV β€” issued by periph error recovery on command timeouts, i.e. precisely when the adapter is misbehaving) is submitted to the adapter as a stale, unrelated command with a reset-SRB payload. The firmware dispatches on Header.Command, so the reset is either silently mangled into a different operation (up to container reconfiguration if the stale opcode maps to a container ioctl) or ignored; aac_cam_complete() then interprets the response as an aac_srb_response. The reset also obviously does not reset the device, defeating CAM's error recovery escalation.

Threat: requires an AAC adapter (PCI) plus a failing/target device that triggers CAM reset escalation; consequences are adapter-state corruption / failed recovery. Device-dependent; not memory-unsafe in the host.

Fix

Set Header.Command on the common path (both XPT_SCSI_IO and XPT_RESET_DEV) β€” fix.diff moves the 64-bit selection before the direction switch. Alternatively clear Header.Command in aac_release_command().

Fix verification

not_testable
↓ fix.diffper-fix-DF-4717

Confirmed kernel references

Detail

Exploit chain

failing/target device behind an AAC controller -> CAM periph timeout -> XPT_RESET_DEV -> FIB sent under stale opcode -> reset lost / adapter command confusion

Evidence (decisive lines)

code trace: aac_cam.c:432-499 (only SCSI_IO sets Command), aac_cam.c:511-518 (XferState/Size set, Command untouched), aac.c:1395-1407 (re-init omits Header.Command)

PoC changes

n/a (no seeded PoC)

Verified recommended fix

assign fib->Header.Command on the common path before the func_code switch (fix.diff) or clear it in aac_release_command()

Verdict

XPT_RESET_DEV submits its FIB with a stale Header.Command (set only for XPT_SCSI_IO at aac_cam.c:452-453; aac_release_command's re-init at aac.c:1395-1407 never clears it), so device resets are dispatched by the firmware under an unrelated opcode (protocol confusion / failed error recovery; worst case container-ioctl misinterpretation). Runtime verification impossible on this guest: Adaptec AAC is PCI hardware not emulated by QEMU. Fix by code trace: set the command on the common path (fix.diff).