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

XPT_RESET_DEV frees a regular-pool command via the high-priority free list, corrupting command-pool accounting

Summary

mprsas_action_resetdev() at mpr_sas.c:3351: tm=mpr_alloc_command(sc) (regular pool req_list). Sent with HighPriority descriptor at :3370. Completion mprsas_resetdev_complete at :3384 -> mprsas_free_tm at :3425 -> mprsas_free_tm at :244 -> mpr_free_high_priority_command at :269 (high_priority_req_list). Pool mismatch: regular alloc, high-priority free. Each reset leaks 1 regular slot + corrupts high-pri list with out-of-range SMID. After N resets mpr_alloc_command returns NULL -> disk stall. Trigger: camcontrol reset da0 (operator priv). Fix: use mprsas_alloc_tm(sc) instead of mpr_alloc_command(sc).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1223 Β· 9 files
FileTypeDescriptionSize
VERDICT.md verdict full narrative + path:line trace 4.5 KB ↓ raw
README.md readme reproduce / preconditions / PoC snippet 1.2 KB ↓ raw
fix.diff suggested-fix use mprsas_alloc_tm(sc) instead of mpr_alloc_command(sc) 478 B view raw
build.sh build-log nativekernel build validation 250 B view raw
run.sh run-log no runtime trigger; static trace 154 B view raw
env.txt environment guest uname, PCI devices, GENERIC config 587 B view raw
build.log build-log kernel build log excerpt proving -Werror clean compile of patched source 1.1 KB view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme reproduce / preconditions / PoC snippet
↓ download raw

DF-1223 β€” mpr XCT_RESET_DEV pool mismatch

Reproduce

Not runnable on the audit guest β€” no LSI MPT-Fusion SAS controller is attached. The verification is a static source trace + a compiling fix.diff.

To validate the fix compiles, after applying fix.diff to /usr/src:

cd /usr/src
make -j6 nativekernel KERNCONF=X86_64_GENERIC

Bug location

sys/dev/raid/mpr/mpr_sas.c:3351 β€” mprsas_action_resetdev() allocates with mpr_alloc_command(sc) (regular pool) but frees via mprsas_free_tm β†’ mpr_free_high_priority_command (high-pri pool). Every other TM path uses mprsas_alloc_tm(sc). Each reset leaks a regular slot and corrupts the high-pri list with out-of-range SMIDs; after N resets, mpr_alloc_command returns NULL β†’ disk I/O stall.

Trigger preconditions (NOT met on this guest)

  • An LSI MPT-Fusion SAS controller (mpr-attached) with a target.
  • A user able to issue camcontrol reset <target> (operator group).

On a system WITH an mpr target, the PoC is:

for i in $(seq 1 200); do camcontrol reset da0; done
# dmesg: "command alloc failure in mprsas_action_resetdev"; disk I/O stalls

Files

  • VERDICT.md, fix.diff, env.txt, build.log.
VERDICT.md verdict full narrative + path:line trace
↓ download raw

DF-1223 β€” mpr XPT_RESET_DEV pool mismatch (regular alloc, high-pri free)

Verdict

INCONCLUSIVE (not_testable on this guest) β€” bug confirmed in source; the LSI MPT-Fusion SAS controller is absent from the audit guest. Fix authored, applied, and compiled clean as part of a single-fix nativekernel build.

Finding summary

mprsas_action_resetdev() in sys/dev/raid/mpr/mpr_sas.c allocates the task from the regular command pool at :3351:

tm = mpr_alloc_command(sc);   /* pops from sc->req_list */

…then sends the request with a HighPriority descriptor at :3370 (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY), and on completion the path mprsas_resetdev_complete (:3384) β†’ mprsas_free_tm (:3425) β†’ mpr_free_high_priority_command (mprvar.h:642) frees it back to the high-priority pool (sc->high_priority_req_list).

Every other task-management path in this driver uses mprsas_alloc_tm(sc) (mpr_sas.c:234 β†’ mpr_alloc_high_priority_command), so the alloc/free pools match. Only mprsas_action_resetdev uses mpr_alloc_command directly β€” that is the bug. Each device reset: 1. Leaks one slot from the regular req_list (it never comes back). 2. Pushes an out-of-range SMID command onto the high-priority list (the regular alloc gives SMIDs in the regular-command range, which the high-pri free assumes are in the high-pri SMID range). 3. After N resets, mpr_alloc_command returns NULL β†’ all disk I/O on the controller stalls β†’ persistent DoS until reload/reboot. The corrupted high-pri list can also cause downstream KKASSERTs / use-after-free on the next high-pri task (the KASSERT at mprvar.h:654 may catch some shapes).

Triggered by camcontrol reset da0 (operator/cambreaker privilege β€” typically operator group or root; reachable from any user in operator).

Source confirmation (audited tree)

  • mpr_sas.c:3351 β€” tm = mpr_alloc_command(sc); β€” pops sc->req_list.
  • mpr_sas.c:3370–3371 β€” tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; β€” sent as high-priority.
  • mpr_sas.c:3425 β€” mprsas_free_tm(sc, tm); β€” completion frees via the helper.
  • mpr_sas.c:244–270 β€” mprsas_free_tm() β†’ mpr_free_high_priority_command(sc, tm); (line 269) β€” pushes sc->high_priority_req_list.
  • mprvar.h:605–619 β€” mpr_alloc_command β€” pops req_list.
  • mprvar.h:622–643 β€” mpr_free_high_priority_command β€” pushes high_priority_req_list.
  • mprvar.h:646–660 β€” mpr_alloc_high_priority_command β€” pops high_priority_req_list.
  • mpr_sas.c:234 β€” mprsas_alloc_tm() β†’ mpr_alloc_high_priority_command(sc); β€” the correct helper.
  • mpr_sas.c:461, 524, 1723 β€” every other TM path uses mprsas_alloc_tm(sc). Only the reset path uses the regular alloc.

Why not runtime-reproduced on this guest

The QEMU/KVM guest has no LSI MPT-Fusion SAS controller (only virtio-blk for storage). The mpr driver never attaches, no mpr_softc is created, no SIM is registered with CAM, and camcontrol reset would target nothing. The mpr.ko module is present but not loaded. The bug path is unreachable at runtime on this guest.

Triggering the bug requires a real LSI MPT-Fusion SAS controller (or an emulated one) with an attached target, and an unprivileged-ish user able to issue camcontrol reset <target> (typically via the operator group, which on multi-user systems is often granted to semi-trusted users).

Fix (fix.diff)

Use the existing mprsas_alloc_tm() helper, which allocates from the high-priority pool that the completion path expects:

-    tm = mpr_alloc_command(sc);
+    tm = mprsas_alloc_tm(sc);

One-line change, exactly matches what every other TM path in the same file already does (mprsas_action_bus_reset, mprsas_action_target_reset, mprsas_scsiio_timeout, etc. all use mprsas_alloc_tm(sc)). After the fix, the alloc and free pools match: high-pri alloc, high-pri free.

Fix validation

  • git apply --check -p1 β€” clean.
  • Applied to in-guest /usr/src, built with make -j6 nativekernel (with DF-1212 and DF-1214) β€” compiled clean, NK_DONE rc=0.
  • fix_status: not_testable β€” no LSI SAS hardware on this guest; cannot do a runtime before/after camcontrol reset demonstration.

Run / reproduce

Not runnable on this guest. On a system with an mpr-attached SAS target, the PoC is:

for i in $(seq 1 N); do camcontrol reset da0; done
# after enough iterations: disk I/O stalls, mpr_alloc_command returns NULL,
# and dmesg shows "command alloc failure in mprsas_action_resetdev"

Fix verification

not_testable

compile validated

kernel/module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. mprsas_action_resetdev regular-pool alloc + high-pri free -> pool mismatch + DoS. mpr in GENERIC, no SAS HW.