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

ISP_TSK_MGMT ioctl (24xx) double-releases the FC scratch buffer on the error path, breaking fcbsy mutual exclusion

Summary

After isp_control(ISPCTL_RUN_MBOXCMD), ispioctl releases FC_SCRATCH_RELEASE(isp, chan) at :665 and, on any firmware failure, releases it again at :667. FC_SCRATCH_RELEASE is a raw fcbsy=0, so the second release destroys the ownership of any concurrent legitimate holder (FC kthread fabric/portdb scans, a second ioctl), letting two contexts share fcp->isp_scratch as the DMA target of MBOX_EXEC_COMMAND_IOCB_A64 - IOCBs and firmware responses get cross-delivered between unrelated mailbox commands. Root-only (/dev/ispN 0600) on 24xx/25xx hardware; corrupted scan/PDB/TMF data within the bounded scratch allocation - wrong abort/reset decisions, DoS-flavored misbehavior, no direct OOB. The double release is literal; one-line delete fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3659 Β· 8 files
FileTypeDescriptionSize
trig_note.c β€” 1.7 KB view raw
build.sh β€” 90 B view raw
run.sh β€” 95 B view raw
build.log β€” 55 B view raw
run.log β€” 173 B view raw
env.txt β€” 386 B view raw
fix.diff β€” 341 B view raw
VERDICT.md β€” 2.3 KB ↓ raw
VERDICT.md
↓ download raw

DF-3659 VERDICT β€” double FC_SCRATCH_RELEASE in ISP_TSK_MGMT

Status: not_reproduced (no QLogic hardware on guest; defect is literal in source)

Defect

sys/dev/disk/isp/isp_freebsd.c:665 releases the per-channel FC scratch (fcbsy = 0, expansion at isp_freebsd.h:380), and line 667 releases it again on the same path whenever retval != 0 || sp->req_completion_status != 0. Every other scratch consumer in the driver (see isp.c:4557-4581 RESET_DEV, isp.c:4658-4682 ABORT_CMD) is careful to release exactly once per acquire.

Race window and consequence

  1. T1 (root ioctl thread) runs a 24xx task-management command; firmware fails it.
  2. Line 665 clears fcbsy.
  3. T2 (FC kthread in isp_fc_runstate β†’ isp_scan_fabric / isp_scan_loop / isp_pdb_sync, or a concurrent ioctl) sees fcbsy == 0, acquires, starts a mailbox command DMAing into fcp->isp_scratch.
  4. Line 667 clears fcbsy again β€” T2's ownership is destroyed.
  5. T3 acquires and overlaps T2: two mailbox commands share one DMA scratch; IOCBs and firmware responses are cross-delivered.

Impact ceiling: corrupted scan/PDB/TMF data within the bounded scratch allocation β€” wrong loop ids/WWNs fed into the port database and task management, i.e. functional corruption and DoS, not a memory-safety break. Trigger is root-only (/dev/ispN 0600, isp_freebsd.c:229) on 24xx/25xx hardware with an unlucky interleaving.

Why not reproduced

Guest has zero QLogic PCI devices; /dev/isp* does not exist, so the ioctl entry point cannot even be opened (run.log). The defect itself is provable by inspection and the fix is a one-line deletion.

Fix

Remove the duplicate release at line 667 (fix.diff). After the fix the error path matches the success path and the isp.c discipline.

Fix validation

not_testable β€” the affected code path requires ISP 24xx hardware plus a firmware-failing TMF racing the kthread; neither exists on this guest.

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

one-line deletion restores single-release discipline; cannot be behaviorally validated without 24xx hardware.

findings/poc/DF-3659/fix.diff
↓ fix.diffper-fix-DF-3659

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (line-accurate sequence); fix.diff (deletes the duplicate release); run.log (no /dev/isp* on guest)

PoC changes

documentation-only trigger (runtime sequence needs 24xx HBA); compiled and ran the no-op on the guest to record absence

Verified recommended fix

Delete the duplicate FC_SCRATCH_RELEASE at isp_freebsd.c:667.

Verdict

ispioctl ISP_TSK_MGMT 24xx branch releases the FC scratch twice on the error path (isp_freebsd.c:665 then :667; FC_SCRATCH_RELEASE is a raw fcbsy=0 per isp_freebsd.h:380). A concurrent legitimate scratch holder (FC kthread scan or second ioctl) between the two releases loses ownership, allowing two contexts to share fcp->isp_scratch as the DMA target of MBOX_EXEC_COMMAND_IOCB_A64 -> IOCB/response cross-delivery between unrelated mailbox commands. Root-only 0600 cdev, 24xx hardware required, bounded to the scratch buffer (no OOB). Guest has no QLogic device and no /dev/isp* -> not_reproduced, device absent.