XPT_ABORT passes the abort CCB itself to ISPCTL_ABORT_CMD - every SCSI command abort fails (upstream passes accb)
Summary
isp_action(XPT_ABORT) calls isp_control(isp, ISPCTL_ABORT_CMD, ccb) with the XPT_ABORT control CCB instead of ccb->cab.abort_ccb (accb). isp_control looks the command up via isp_find_handle() which matches by pointer against isp->isp_xflist[] - a table only isp_start() fills - so the lookup always returns ISP_HANDLE_FREE, the driver logs 'cannot find handle for command to abort' and returns -1, and the XPT_ABORT is completed CAM_UA_ABORT. Aborts never succeed on any chip generation. CAM periph error recovery (da/cd timeout ladder: retry -> abort -> BDR) loses its abort rung: any timed-out command escalates straight to device/bus reset, disrupting every LUN/target on the channel. Deterministic functional break on all isp-equipped hosts; in-kernel trigger, no memory corruption. One-word fix matching upstream FreeBSD. VERIFIED vs FreeBSD HEAD (fetched live). Guest: no QLogic HBA - not_reproduced, source-certain; live /dev/xpt0 trigger proves userland can't inject XPT_ABORT past the xptioctl whitelist (errno 45).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3657 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trig_abort.c | β | 2.6 KB | view raw | |
| build.sh | β | 92 B | view raw | |
| run.sh | β | 96 B | view raw | |
| build.log | β | 76 B | view raw | |
| run.log | β | 353 B | view raw | |
| env.txt | β | 386 B | view raw | |
| fix.diff | β | 341 B | view raw | |
| VERDICT.md | β | 3.8 KB | β raw |
DF-3657 VERDICT β XPT_ABORT wrong CCB
Status: not_reproduced (guest has no QLogic HBA; source-level certain)
The bug
sys/dev/disk/isp/isp_freebsd.c:4531-4557, case XPT_ABORT:
union ccb *accb = ccb->cab.abort_ccb;
switch (accb->ccb_h.func_code) {
...
case XPT_SCSI_IO:
error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); /* BUG: ccb, not accb */
Upstream FreeBSD (sys/dev/isp/isp_freebsd.c, case XPT_ABORT) passes
accb β verified against current FreeBSD HEAD:
case XPT_SCSI_IO:
error = isp_control(isp, ISPCTL_ABORT_CMD, accb);
Why the abort always fails β full trace
isp_control(ISPCTL_ABORT_CMD, xs)atisp.c:4603takesxsand callshandle = isp_find_handle(isp, xs)(isp.c:4611).isp_find_handle()(isp_library.c:260-274) matches by pointer equality againstisp->isp_xflist[i].cmdβ the table filled only byisp_start()for commands actually queued to the firmware.- The XPT_ABORT CCB is a synchronous control CCB; it was never passed to
isp_start(), so it is in noisp_xflist[]slot βISP_HANDLE_FREE(0). isp.c:4612-4616:handle == 0β warning "cannot find handle for command to abort" βbreakβisp_controlreturns-1(isp.c:4845).isp_freebsd.c:4542-4546: error βccb->ccb_h.status = CAM_UA_ABORT.
Net effect: every XPT_ABORT of an XPT_SCSI_IO on isp(4) fails, on all chip generations (the IS_24XX / IS_FC / SCSI branches are all behind the failed handle lookup). The command that should have been aborted keeps running; callers must escalate to XPT_RESET_DEV / XPT_RESET_BUS.
Impact
- CAM periph error recovery (
cam_periph_timeoutβ abort β BDR ladder) degrades: the abort rung always fails, so any timed-out command forces device reset / bus reset escalation. More resets = more I/O disruption on a live bus (all LUNs/targets on the channel see the reset). - No memory corruption: the wrongly-passed CCB is only used for the (failing)
pointer lookup;
tgt/chanderived from it stay in range because they come from the (valid) abort path. - Callers are in-kernel CAM periphs (da/cd/...), or
camcontrol abortstyle tools on hosts with the HBA. DragonFly userland/dev/xpt0cannot issue XPT_ABORT at all (xptioctl whitelist,sys/bus/cam/cam_xpt.c:1028+) β demonstrated live inrun.log(errno 45, CCB never reaches a SIM).
Why not reproduced on the guest
pciconf -l: zero devices with vendor 0x1077 β isp(4) never attaches, no/dev/isp*, no isp CAM bus (bus0 is ATAPI). The cited path (isp_actionXPT_ABORT) cannot execute at runtime on this guest.- Verification is therefore the source-level trace above plus the upstream FreeBSD diff β both conclusive.
Fix validation
fix.diff (ccb β accb) is the minimal, upstream-equivalent fix. Fix build/boot
validation on the guest is not_testable: with no HBA the fixed kernel
cannot execute the abort path (nothing to observe); a compile check alone
would prove nothing beyond syntax. The one-word change is byte-identical in
effect to upstream FreeBSD's code, which has shipped for a decade.
Kernel references
- sys/dev/disk/isp/isp_freebsd.c:4531-4557 (XPT_ABORT case; wrong arg at 4541)
- sys/dev/disk/isp/isp.c:4603-4616 (ISPCTL_ABORT_CMD; handle lookup)
- sys/dev/disk/isp/isp.c:4845 (isp_control fallthrough return -1)
- sys/dev/disk/isp/isp_library.c:260-274 (isp_find_handle pointer match)
- sys/bus/cam/cam_xpt.c:1028+ (userland CAMIOCOMMAND whitelist β no XPT_ABORT)
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_testablefix.diff is the upstream-equivalent one-word correction; cannot be runtime-validated on this guest (no HBA to attach, no isp bus in CAM), so fix validation is not_testable rather than fixed.
findings/poc/DF-3657/fix.diff; FreeBSD HEAD sys/dev/isp/isp_freebsd.c case XPT_ABORT passes accb (verified via raw.githubusercontent fetch 2026-09-15)
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (userland XPT_ABORT rejected pre-SIM, bus0=ATAPI, 0 QLogic devices); VERDICT.md (full path:line trace); fix.diff (one-word fix matching upstream)
PoC changes
wrote reachability trigger from scratch (seed sketch had none); documented xptioctl whitelist boundary live on guest
Verified recommended fix
Pass accb (ccb->cab.abort_ccb) to isp_control(ISPCTL_ABORT_CMD) at isp_freebsd.c:4541, matching upstream FreeBSD.
Verdict
isp_action(XPT_ABORT) passes the XPT_ABORT CCB itself (ccb) to isp_control(ISPCTL_ABORT_CMD) instead of ccb->cab.abort_ccb (accb) at isp_freebsd.c:4541; upstream FreeBSD passes accb. isp_find_handle() matches by pointer against isp_xflist[] which only holds started commands, so the lookup always fails and every XPT_ABORT of an XPT_SCSI_IO returns CAM_UA_ABORT. CAM error-recovery (periph timeout abort rung) is broken; escalation always goes to device/bus reset. Runtime reproduction impossible on this guest: zero QLogic PCI devices (vendor 0x1077), no /dev/isp*, no isp CAM bus; userland /dev/xpt0 CAMIOCOMMAND whitelist also excludes XPT_ABORT (demonstrated in run.log, errno 45). Source-level trace + upstream diff are conclusive.
No comments yet.