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

Error paths permanently strand reserved request slots and hang CAM ccbs: AEN/param channels die; map-failed ccb is on no queue and can never be completed

Summary

(1) The send/set/get error paths return leaving reserved slots BUSY forever - the AEN fetch channel and param channel die permanently (the get_param slot has cb=NULL, uncompletable). (2) tws_execute_scsi returns on map failure without xpt_done or slot release; the request is on NO queue (submission inserts it), so the reset drain can never rescue it - permanent ccb leak + hung transaction, with the pre-armed callout turning a software failure into a full controller reset. Complete+release fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4403 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.1 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
trigger.c β€” 633 B view raw
fix.diff β€” 2.6 KB view raw
verdict.json β€” 2.5 KB view raw
env.txt β€” 541 B view raw

DF-4403 β€” Reserved request slots and CAM ccbs leaked on error paths (permanent AEN/param channel death; hung ccb)

What

Four error paths in sys/dev/raid/tws/tws_cam.c abandon resources:

  1. tws_send_scsi_cmd() :841-844: req->data = kmalloc(TWS_SECTOR_SIZE, M_NOWAIT|M_ZERO); on failure returns ENOMEM while reqs[1] (the AEN fetch slot) stays TWS_REQ_STATE_BUSY forever β€” reserved slots are gated by state alone and are never on FREE_Q (tws.c:631-632, 760-769), so the asynchronous-event channel is permanently dead: no AEN ever fetched again (each tws_aen_complete re-arms via tws_fetch_aen, tws_cam.c:472-476).
  2. tws_set_param() :868-872: same for reqs[3] β€” the parameter channel dies; tws_aen_synctime_with_host and shutdown tws_init_connect paths degrade.
  3. tws_execute_scsi() :807-810: the ccb timeout callout is armed at :807 and tws_map_request() may fail (e.g. bus_dmamap_load ENOMEM / EFBIG); the error is returned to tws_action (:271-272, trace only) without xpt_done(ccb) and without releasing the request β€” the request slot is off FREE_Q forever (it is only re-queued by completion paths), the ccb is never completed, and because the request was never submitted it is not on BUSY_Q either (tws_submit_command inserts, tws_hdm.c:326-330), so even the reset drain (tws_drain_busy_queue, tws_cam.c:616-643) can never complete it: permanent ccb leak + hung upper-layer transaction (its callout eventually fires tws_timeout β†’ full controller reset for a software failure).
  4. tws_get_param() :918-921: same slot-stranding pattern for reqs[3] when the sector kmalloc fails (req->cb == NULL there, so nothing can ever complete it).

Impact

Progressive driver degradation β†’ CAM resource exhaustion β†’ I/O DoS on the adapter under sustained memory pressure; loss of AEN delivery (array events/alarms silently stop). Requires kmalloc/dmamap failure β€” Low severity, but the leaks are permanent (no recovery path).

Status

QEMU guest has no 3ware HBA β†’ inconclusive; each leak path proven from source.

VERDICT.md
↓ download raw

VERDICT β€” DF-4403

Status: inconclusive (hardware-gated).

Proof (per path)

  1. Reserved-slot lifecycle. tws_get_request() marks slots 0-3 BUSY by state only (tws.c:749-789); only slots β‰₯ TWS_RESERVED_REQS ever live on FREE_Q (tws.c:631-632). A reserved slot is freed by setting state = TWS_REQ_STATE_FREE (e.g. tws_getset_param_complete, tws_cam.c:392-394). If an allocation between get and submit fails, none of those completion paths runs: - tws_send_scsi_cmd tws_cam.c:842-844 β†’ reqs[1] (AEN) stuck BUSY; - tws_set_param :870-872 and tws_get_param :919-921 β†’ reqs[3] (GETSET_PARAM) stuck BUSY (get_param additionally hascb == NULL,:924, so even a spurious firmware response can never complete it). Every futuretws_get_request(type)for that type returns NULL (tws.c:763-764) β€” the channel is dead for the life of the adapter. AEN death specifically silencestws_aen_completeβ†’tws_fetch_aenre-arm (tws_cam.c:472-476`), i.e. array event delivery stops silently.
  2. SCSI IO slot + ccb. tws_execute_scsi removes a request from FREE_Q at :719 (tws.c:792-794) and arms the ccb callout at :807. If tws_map_request fails (:809 β€” bus_dmamap_load ENOMEM; the EINPROGRESS case is converted at :995-999 and is fine), the function returns the error; tws_action only traces (:271-272). The ccb has CAM_SIM_QUEUED set (:717) and is never xpt_doned; the request is on no queue (BUSY_Q insertion happens only in tws_submit_command, tws_hdm.c:326-330, never reached), so tws_drain_busy_queue (tws_cam.c:616-643) cannot rescue it during a reset either. The upper layer waits forever; the armed callout (:807) fires tws_timeout β†’ tws_reset β†’ adapter-wide disruption β€” for a software allocation failure.

Trigger

Kernel memory pressure (M_NOWAIT sector kmallocs / dmamap load failure) on a host with the controller. Not reproducible on the audit guest.

Impact ceiling

Permanent (unrecoverable) resource loss per event: AEN/param channels die; CAM transactions hang with no completion path. DoS-grade degradation, gated on allocation failure β†’ Low.

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 adds the missing unwind on all four paths; not validatable on the guest (no controller, uncontrollable allocation failure).

fix.diff
↓ fix.diffper-fix-DF-4403

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (per-path proof); fix.diff (free reserved slots on alloc failure; complete+release ccb on map failure); trigger.c (conditions)

PoC changes

no seed; conditions stub only

Verified recommended fix

Return reserved slots to FREE state on allocation failure and complete+release the ccb when tws_map_request fails in tws_execute_scsi.

Verdict

Certain from source, hardware/allocation-failure gated. (1) tws_send_scsi_cmd (:842-844), tws_set_param (:870-872) and tws_get_param (:919-921) strand reserved slots reqs[1]/reqs[3] in TWS_REQ_STATE_BUSY when their sector kmalloc fails β€” reserved slots are gated by state alone (tws.c:749-789, 631-632), so the AEN fetch channel and the parameter channel die permanently (get_param's slot even has cb==NULL at :924, so nothing can ever complete it). (2) tws_execute_scsi (:807-810) returns tws_map_request failure to tws_action (:271, trace only) without xpt_done and without releasing the slot; the request was never submitted so it is on no queue (BUSY_Q insert happens only in tws_submit_command, tws_hdm.c:326-330) and even the reset drain (tws_cam.c:616-643) can never complete the ccb β€” permanent CAM transaction hang plus slot leak, and the pre-armed callout turns the software failure into a full controller reset. Guest lacks the HBA; each path proven from source in VERDICT.md.