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

mpssas_resetdev_complete dereferences a NULL task-management reply: XPT_RESET_DEV outstanding during a Diag Reset panics the kernel (DF-3801 twin)

Summary

mpssas_resetdev_complete is the only TM completion handler in the file without a cm_reply==NULL guard: it falls straight to kprintf(resp->IOCStatus, resp->ResponseCode) with resp==NULL. complete_all_commands sets cm_reply=NULL and invokes cm_complete for every outstanding command during a Diag Reset, so an XPT_RESET_DEV in flight when recovery escalates to a controller reset reads near NULL and panics. All six sibling handlers carry the guard. Any failing/recovering mps-attached device whose error recovery overlaps a device reset with a controller diag reset - the standard recovery storm a dying disk produces. Guard fix (the accepted mpr DF-3801 pattern).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3893 Β· 8 files
FileTypeDescriptionSize
README.md β€” 2.4 KB ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
run.sh β€” 111 B view raw
run.log β€” 482 B view raw
env.txt β€” 274 B view raw
fix.diff β€” 970 B view raw
verdict.json β€” 2.4 KB view raw
manifest.json β€” 698 B view raw

DF-3893 β€” mpssas_resetdev_complete dereferences a NULL task-management reply: XPT_RESET_DEV outstanding during a Diag Reset panics the kernel

File: sys/dev/raid/mps/mps_sas.c (pass 2). Twin of DF-3801 (mpr_sas.c).

What

mpssas_resetdev_complete (sys/dev/raid/mps/mps_sas.c:2791-2834) is the completion handler for XPT_RESET_DEV task-management commands. Unlike every other TM completion handler in the file, it has no cm_reply == NULL guard before dereferencing the reply:

        resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;   /* :2799 */
        ccb = tm->cm_complete_data;

        if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {  /* :2807 */
                ... goto bailout;
        }

        kprintf("%s: IOCStatus = 0x%x ResponseCode = 0x%x\n",
            __func__, resp->IOCStatus, resp->ResponseCode);   /* :2819-2820 NULL deref */

tm->cm_reply is NULL whenever the command is completed by mpssas_complete_all_commands (sys/dev/raid/mps/mps_sas.c:1030-1073), which explicitly sets cm->cm_reply = NULL (line 1042) and then invokes cm->cm_complete(sc, cm) (line 1053) for every outstanding command during a diagnostic reset (mpssas_handle_reinit β†’ mpssas_complete_all_commands, mps_sas.c:1098; called from mps_reinit, mps.c:317).

Sibling handlers all guard this exact case: - mpssas_logical_unit_reset_complete mps_sas.c:1160-1173 - mpssas_target_reset_complete mps_sas.c:1246-1259 - mpssas_abort_complete mps_sas.c:1378-1392 - mpssas_remove_device mps_sas.c:557-563, mpssas_remove_complete mps_sas.c:627-633, mpssas_remove_volume mps_sas.c:360-366

Impact

Kernel NULL-pointer read (resp->IOCStatus at a small struct offset) β†’ panic. Trigger: any XPT_RESET_DEV in flight (issued by CAM error recovery when a da(4) LUN keeps timing out, or by camcontrol reset) at the moment error recovery escalates to a controller diagnostic reset (mps_reinit, e.g. via mpssas_tm_timeout mps_sas.c:1131 or a NULL TM reply escalation at mps_sas.c:1170/1256/1389). This is precisely the recovery storm a failing disk produces.

Build/run

run.sh records the hardware gate (no SAS2 HBA in the guest; driver compiled in but SIM never attaches). Source-level certainty: total.

Fix

Add the missing NULL guard, completing the CCB with CAM_SCSI_BUS_RESET and freeing the TM (see fix.diff) β€” identical to the guard pattern of the three sibling handlers and to the DF-3801 fix in mpr(4).

VERDICT.md
↓ download raw

DF-3893 β€” VERDICT

Status: not_reproduced (hardware-gated) β€” defect certain from source. Impact class (on real hardware): kernel panic via NULL deref (DoS).

Proof chain

  1. sys/dev/raid/mps/mps_sas.c:2784-2787 β€” XPT_RESET_DEV is dispatched by mpssas_action (mps_sas.c:981-984) to mpssas_action_resetdev, which sends a TARGET_RESET TMF with tm->cm_complete = mpssas_resetdev_complete (mps_sas.c:2784).
  2. mps_reinit (sys/dev/raid/mps/mps.c:285-363) runs on error-recovery escalation paths β€” mpssas_tm_timeout (mps_sas.c:1131), NULL-reply escalations in mpssas_logical_unit_reset_complete (mps_sas.c:1170), mpssas_target_reset_complete (mps_sas.c:1256), mpssas_abort_complete (mps_sas.c:1389), and the failed-target-reset escalation (mps_sas.c:1289).
  3. mps_reinit β†’ mpssas_handle_reinit (mps.c:317, mps_sas.c:1076) β†’ mpssas_complete_all_commands (mps_sas.c:1098), which for every outstanding command sets cm->cm_reply = NULL (mps_sas.c:1042) and calls cm->cm_complete(sc, cm) (mps_sas.c:1053).
  4. For an in-flight XPT_RESET_DEV, cm_complete is mpssas_resetdev_complete. Its ERROR_MASK branch (mps_sas.c:2807) is not taken (TM commands have no SGL, so CHAIN_FAILED cannot be set β€” the code's own comment at mps_sas.c:2802-2806 says so). It falls straight to kprintf(... resp->IOCStatus, resp->ResponseCode) at mps_sas.c:2819-2820 with resp == NULL β†’ read at NULL+offset β†’ kernel panic.
  5. Contrast: all six other TM/IOU completion handlers in this file have the reply == NULL guard (mps_sas.c:360, 557, 627, 1160, 1246, 1378). mpssas_resetdev_complete is the lone omission β€” exactly the asymmetry that was DF-3801 in mpr_sas.c (fixed there by adding the guard).

Why not reproduced on this guest

No LSI SAS2 HBA exists in QEMU (env.txt, run.log): mps_attach never runs, no SIM registers with CAM, and XPT_RESET_DEV can never be routed to mpssas_action. Reproduction would need: real HBA + camcontrol reset (or a da LUN in deep error recovery) racing a controller diag reset; expected result: Fatal trap 12: page fault while in kernel mode reading near NULL in mpssas_resetdev_complete.

Exploitability

DoS/panic only (NULL-page read). No escalation path; not pursued beyond classification because the primitive is a fixed near-NULL read.

Fix validation

Not testable on this guest. fix.diff adds the same guard the three sibling handlers (and the mpr DF-3801 fix) use; behavior then matches mpssas_target_reset_complete's DIAGRESET cleanup path.

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 guard; cannot be exercised on this guest (no SAS2 HBA).

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

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (path:line proof incl. all sibling guards), env.txt (no SAS2 HBA), run.sh + run.log (hardware gate), fix.diff (NULL guard completing ccb with CAM_SCSI_BUS_RESET)

PoC changes

Trigger requires an XPT_RESET_DEV racing a controller diag reset on real SAS2 hardware; run.sh records the hardware gate.

Verified recommended fix

Add resp == NULL guard completing the CCB with CAM_SCSI_BUS_RESET and freeing the TM, matching mpssas_target_reset_complete.

Verdict

mpssas_resetdev_complete (sys/dev/raid/mps/mps_sas.c:2791) is the only TM completion handler in the file without a cm_reply==NULL guard; it dereferences resp->IOCStatus/resp->ResponseCode at mps_sas.c:2819-2820 immediately after the ERROR_MASK check. mpssas_complete_all_commands (mps_sas.c:1030-1073) invokes cm_complete with cm_reply=NULL (set at mps_sas.c:1042) for every outstanding command during a Diag Reset (mpssas_handle_reinit, mps_sas.c:1098, called from mps_reinit at mps.c:317), so an XPT_RESET_DEV TM outstanding at reset time reads near NULL and panics. All six sibling handlers have the guard (mps_sas.c:360, 557, 627, 1160, 1246, 1378). Not reproduced: guest has no SAS2 HBA (env.txt), the mps SIM never attaches, and XPT_RESET_DEV cannot reach mpssas_action. Missing guard is certain from source.