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

mprsas_send_abort/mprsas_send_reset early -1 returns are ignored by every caller: targ->tm stays set forever, the timed-out CCB is never completed (unkillable I/O hang) and the high-priority TM leaks

Summary

When targ->handle == 0, mprsas_send_abort and mprsas_send_reset return -1 without freeing the TM or clearing targ->tm. All callers ignore the return. Since only TM completion handlers clear targ->tm and the TM was never sent, it never completes: subsequent timeouts pile into timedout_commands forever and the triggering ccb is never returned to CAM. Reachable because mprsas_remove_volume clears targ->handle without draining targ->commands, unlike mprsas_remove_device. Hot removal/volume deletion racing command timeouts (normal storage-box events): the issuing thread blocks forever on I/O that can never complete and each wedge leaks one high-priority command. Hardware-gated; fix.diff unwinds the -1 path + drains.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3802 Β· 7 files
FileTypeDescriptionSize
README.md β€” 211 B ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
run.sh β€” 111 B view raw
env.txt β€” 855 B view raw
fix.diff β€” 1.9 KB view raw
verdict.json β€” 2.2 KB view raw
manifest.json β€” 464 B view raw

DF-3802 evidence pack

TM early-return -1 paths wedge the target (targ->tm stuck, timedout ccbs never completed) and leak the TM. Read VERDICT.md. verdict.json: not_reproduced (no SAS3 hardware on guest).

VERDICT.md
↓ download raw

DF-3802 β€” TM error-path early returns wedge the target forever and leak the TM

  • File: sys/dev/raid/mpr/mpr_sas.c
  • Class: unkillable I/O hang (ccb never completed) + high-priority command leak
  • Severity: Medium / Confidence: likely
  • Status on guest: not_reproduced β€” no SAS3 HBA (see env.txt)

Defect

mprsas_send_abort and mprsas_send_reset have early -1 return paths that neither free the task-management command nor clear targ->tm:

  • mpr_sas.c:1609-1615 β€” send_abort: targ = cm->cm_targ; if (targ->handle == 0) return -1;
  • mpr_sas.c:1456-1461 β€” send_reset: if (target->handle == 0) ... return -1;
  • mpr_sas.c:1489-1492 β€” send_reset: unknown type β†’ return -1

Every caller ignores the return value:

  • mpr_sas.c:1731 (mprsas_scsiio_timeout: targ->tm = mprsas_alloc_tm(sc); ... mprsas_send_abort(sc, targ->tm, cm);)
  • mpr_sas.c:1351, mpr_sas.c:1584 (mprsas_send_abort re-arm from logical_unit_reset_complete / abort_complete)
  • mpr_sas.c:1365, mpr_sas.c:1594 (mprsas_send_reset escalation)

After the early return:

  1. targ->tm remains set forever (only the TM completion handlers clear it β€” mpr_sas.c:1307/1353/1403/1431/1553/1578 β€” and the TM was never sent, so it never completes; mprsas_free_tm is never called).
  2. The timed-out command stays on targ->timedout_commands in state MPR_CM_STATE_TIMEDOUT with its ccb never completed β€” once a ccb is in the SIM, CAM has no further timeout for it; the issuing thread hangs forever.
  3. Every subsequent timeout on the target takes the targ->tm != NULL path (mpr_sas.c:1716-1722) and piles more never-aborted commands.
  4. The allocated high-priority command leaks (pool shrink under repetition).

Reachable trigger (provable sequence)

Outstanding I/O to a RAID volume that is deleted:

  • mprsas_prepare_volume_remove sends the reset TM (mpr_sas.c:439-490), but mprsas_remove_volume clears targ->handle = 0 on success (mpr_sas.c:411-429) without draining targ->commands β€” unlike mprsas_remove_device, which completes them (mpr_sas.c:621-628).
  • Any command still on targ->commands that times out afterwards runs mprsas_scsiio_timeout β†’ allocates targ->tm β†’ mprsas_send_abort sees targ->handle == 0 β†’ returns -1 β†’ wedge + leak, per above.

The same wedge occurs mid-error-recovery when the handle is cleared while a recovery TM exists (abort escalation at mpr_sas.c:1584/1594).

Fix

Handle the -1 returns: free the TM, clear targ->tm, and complete the timed-out ccbs with CAM_DEV_NOT_THERE; also drain targ->commands in mprsas_remove_volume like mprsas_remove_device does. See fix.diff.

Guest verification

No SAS3 controller in the guest; no mpr SIM registered β†’ the sequence cannot run. Static proof stands. See env.txt.

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 from line-accurate analysis; not exercisable on this guest.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (path:line proof incl. remove_volume asymmetry), env.txt, fix.diff

PoC changes

Trigger needs real SAS3 hardware with a failing/removed device racing CAM timeouts.

Verified recommended fix

On the -1 paths, clear targ->tm, free the TM, and complete queued timedout commands with CAM_DEV_NOT_THERE; drain targ->commands in mprsas_remove_volume.

Verdict

mprsas_send_abort (mpr_sas.c:1609-1615) and mprsas_send_reset (mpr_sas.c:1456-1461,1489-1492) return -1 on a zero dev handle without freeing the TM or clearing targ->tm; all callers (mpr_sas.c:1351,1365,1584,1594,1731) ignore the return. The TM never completes, so targ->tm is never cleared (only completion handlers clear it), every later timeout on the target queues into timedout_commands forever (mpr_sas.c:1716-1722), and the triggering ccb is never returned to CAM β€” an unkillable I/O hang plus a high-priority-command leak. Provable trigger: mprsas_remove_volume clears targ->handle (mpr_sas.c:414) without draining targ->commands (unlike mprsas_remove_device at mpr_sas.c:621-628), so outstanding volume I/O that times out afterwards hits the -1 path. Not reproduced: guest has no SAS3 HBA (env.txt); mpr SIM never attaches.