mrsas_issue_blocked_cmd times out and lets callers release mfi commands still owned by the firmware β late completions land on recycled commands, double-inserting them into the free pool (shared sc->chan ident wakes the wrong waiter)
Summary
mrsas_issue_blocked_cmd returns after 180 s without aborting the firmware-owned command; every caller releases it to the pool (the ioctl path ignores the return and releases unconditionally - reachable by group operator, node 0660). A late completion is routed by the ISR onto the recycled command via sync_cmd_idx: mrsas_wakeup writes a stale cmd_status into a command now owned by another thread, and if the recycler issued a RAID-map sync, complete_mptmfi_passthru releases it AGAIN - the same cmd is inserted twice into the free list so two threads later obtain the identical mfi command (endpoint: double completion / double xpt_done of a shared ccb). mrsas_reset_ctrl's re-fire loop re-fires wrappers whose mfi cmd was released by a timed-out waiter, with no ownership check. The shared sleep ident sc->chan lets wakeup_one wake the wrong waiter. DF-3673/3710 family. Abort-on-timeout + per-command wait-channel fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3844 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| df3844_trigger.c | β | 1.8 KB | view raw | |
| run.log | β | 90 B | view raw | |
| env.txt | β | 260 B | view raw | |
| fix.diff | β | 2.2 KB | view raw | |
| manifest.json | β | 890 B | view raw | |
| verdict.json | β | 4.3 KB | view raw |
DF-3844 β blocked-cmd timeout releases mfi commands while the firmware still owns them
mrsas_issue_blocked_cmd() (mrsas.c:2865) and mrsas_issue_blocked_abort_cmd() (mrsas.c:3465)
time out after 180 s and simply return; every caller then hands the command back to the free
pool (mrsas_release_mfi_cmd, mrsas.c:2571) although the firmware still holds the SMID. The
late completion is routed onto the recycled command via sync_cmd_idx
(mrsas.c:1294-1299 β 2913), waking the wrong waiter with a stale status, and β when the
recycler is a RAID-map sync β double-releasing the command into mrsas_mfi_cmd_list_head
(mrsas.c:2953/2960), duplicating it on the free list so two threads later obtain the same mfi
command (endpoint: double completion / double xpt_done).
Most user-reachable entry: mrsas_passthru ioctl ignores the blocked-cmd return
(mrsas_ioctl.c:301) and releases unconditionally (mrsas_ioctl.c:367); the device node is
0660 root:operator (mrsas.c:790-792).
Reproduce (needs hardware)
See df3844_trigger.c (sketch for equipped machines) and VERDICT.md for the full
source-level interleaving. On the audit guest this cannot run: mrsas is compiled into the
kernel (kldstat -v β 80 pci/mrsas) but no LSI/Avago PCI device exists, so /dev/mrsas0
is absent (run.log).
Files
VERDICT.mdβ full narrative + interleavingdf3844_trigger.cβ trigger sketch for equipped hardwarerun.logβ guest module/hardware presence checkenv.txtβ guest environmentfix.diffβ timeout abort + per-command wait channel + ioctl return checkmanifest.json,verdict.json
VERDICT β DF-3844 (blocked-cmd timeout vs completion: mfi cmd released while firmware-owned)
status: inconclusive (hardware-gated; source-certain defect, timing-dependent manifestation) confidence: likely Β· class: lifetime/serialization defect with pool-corruption endpoint
The defect (source, certain)
mrsas_issue_blocked_cmd() β sys/dev/raid/mrsas/mrsas.c:2865-2900:
after total_time >= max_wait (180 s, MRSAS_INTERNAL_CMD_WAIT_TIME, mrsas.h:1841) it breaks
with retcode = 1 and returns without aborting the command at the firmware and without marking
it abandoned. Every caller then releases the command back to the free pool while the firmware
still owns the outstanding SMID:
mrsas_get_seq_numβ mrsas.c:566-573 (return ignored; memcpy + release)mrsas_shutdown_ctlrβ mrsas.c:3058-3059mrsas_flush_cacheβ mrsas.c:3098-3099mrsas_passthru(ioctl) β mrsas_ioctl.c:301 ignores the return and mrsas_ioctl.c:367 releases unconditionally β reachable by groupoperator(node is 0660 root:operator, mrsas.c:790-792)mrsas_issue_blocked_abort_cmdβ mrsas.c:3493-3516 releases the abort command on timeout the same way, and leaves the victim (sc->aen_cmd,sc->map_update_cmd) outstanding with no retry (AEN channel wedges:mrsas_aen_handlerreturns early at mrsas.c:3608 whilesc->aen_cmd != NULL).
Consequence chain (interleaving)
- Thread A's passthru times out;
mrsas_release_mfi_cmd(cmd_A)puts cmd_A onmrsas_mfi_cmd_list_head(mrsas.c:2571-2582). The mpt passthru wrapper for A (sync_cmd_idx = A->index, mrsas.c:2821) is still outstanding at the firmware. - The firmware completes A late β ISR β
mrsas_complete_cmdPASSTHRU case (mrsas.c:1294-1299) βcmd_mfi = sc->mfi_cmd_list[cmd_mpt->sync_cmd_idx]= cmd_A even though cmd_A is free or already re-issued to thread B βmrsas_complete_mptmfi_passthru(sc, cmd_A, β¦)(mrsas.c:2913). - If cmd_A was recycled by B: B's blocked wait is woken with A's stale status
(
mrsas_wakeup, mrsas.c:3001-3014, writescmd->cmd_statuson a command owned by another thread). If B's command happens to be a map sync (MR_DCMD_LD_MAP_GET_INFO, mbox.b[1]==1), the completion path releases cmd_A at mrsas.c:2953/2960 while B still owns it; B's own completion then releases it again β the same cmd inserted twice intomrsas_mfi_cmd_list_headβ two futuremrsas_get_mfi_cmd()callers receive the same command β concurrent frame use and double completion (doublexpt_doneon a shared ccb is the eventual memory-safety endpoint, viamrsas_cmd_done, mrsas_cam.c:1080-1089). - Independently,
mrsas_reset_ctrl's re-fire loop (mrsas.c:2448-2470) re-fires every mpt cmd withsync_cmd_idx != ULONG_MAXafter OCR β including wrappers whose mfi cmd was already released by a timed-out waiter (no ownership check), feeding step 3 post-reset. - The wait/wake protocol uses one shared ident:
sc->chan = (void*)&cmd; tsleep((void *)&sc->chan, β¦)(mrsas.c:2880-2888, 3011-3012) βwakeup_onemay wake the wrong waiter; the completed command's thread can then sleep to full timeout, extending window 1-3.
Why not reproduced on the guest
Requires a MegaRAID HBA (driver attaches, /dev/mrsas0 exists) and firmware that stalls or
loses a passthrough completion for >180 s while a second ioctl recycles the command. The guest
has mrsas compiled in (kldstat -v β 80 pci/mrsas) but no LSI PCI device and no
/dev/mrsas* node (run.log). Timing-gated even on equipped hardware. Hence inconclusive,
likely confidence (defect structure is source-certain; manifestation is probabilistic).
Fix
fix.diff: (a) abort the command at the firmware on timeout before returning (mirrors Linux
megaraid_sas megasas_internal_reset-style discipline), (b) make the wait channel per-command
(tsleep(cmd) / wakeup_one(cmd)) instead of the shared sc->chan ident.
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 (a) issues an abort on timeout so no caller releases a firmware-owned cmd, (b) fixes the wrong-waiter wakeup by keying the sleep channel on the cmd pointer, (c) makes mrsas_passthru honor the timeout return. Cannot be validated on a guest without the HBA -> not_testable.
fix.diff three hunks (mrsas.c timeout abort + per-cmd ident; mrsas_ioctl.c return check).
Confirmed kernel references
- sys/dev/raid/mrsas/mrsas.c:2865
- sys/dev/raid/mrsas/mrsas.c:2887
- sys/dev/raid/mrsas/mrsas.c:2893
- sys/dev/raid/mrsas/mrsas.c:2571
- sys/dev/raid/mrsas/mrsas.c:1294
- sys/dev/raid/mrsas/mrsas.c:2913
- sys/dev/raid/mrsas/mrsas.c:2945
- sys/dev/raid/mrsas/mrsas.c:2953
- sys/dev/raid/mrsas/mrsas.c:2960
- sys/dev/raid/mrsas/mrsas.c:3011
- sys/dev/raid/mrsas/mrsas.c:2448
- sys/dev/raid/mrsas/mrsas.c:2821
- sys/dev/raid/mrsas/mrsas_ioctl.c:301
- sys/dev/raid/mrsas/mrsas_ioctl.c:367
- sys/dev/raid/mrsas/mrsas_cam.c:1080
Detail
Exploit chain
operator-group ioctl A stalls in FW; A times out at 180 s and releases its mfi cmd; ioctl B (or driver map sync) recycles it; FW completes A late; stale completion wakes B with wrong status or double-releases the map-sync cmd; pool list contains the cmd twice; two later ioctl threads obtain the same mfi cmd; concurrent frame writes + double completion -> corrupted pool / double xpt_done (ccb UAF). Timing-gated, requires the HBA.
Evidence (decisive lines)
VERDICT.md interleaving with path:line for each step; run.log guest presence check (driver in kernel, no HBA, no /dev/mrsas*); df3844_trigger.c outline for equipped hardware.
PoC changes
No seed PoC; authored df3844_trigger.c sketch. Cannot be built/run meaningfully without /dev/mrsas0 (open() fails on the guest β recorded).
Verified recommended fix
On timeout, abort the command at the firmware before returning to callers that release it, and use a per-command sleep ident (tsleep(cmd)/wakeup_one(cmd)) instead of the shared sc->chan.
Verdict
Source-certain lifetime defect: mrsas_issue_blocked_cmd (mrsas.c:2865-2900) returns after its 180 s timeout without aborting the firmware-owned command, and every caller (mrsas.c:566-573, 3058-3059, 3098-3099; mrsas_ioctl.c:301+367 which ignores the return) immediately releases the mfi cmd back to the free pool. A late firmware completion then routes through mrsas_complete_cmd's PASSTHRU case (mrsas.c:1294-1299) onto the recycled command; if the recycler issued a RAID-map sync DCMD, mrsas_complete_mptmfi_passthru releases the command again (mrsas.c:2953/2960) while its new owner still waits, double-inserting it into mrsas_mfi_cmd_list_head (mrsas.c:2578) β two threads subsequently get the same mfi cmd, and the endpoint is a double completion/double xpt_done of a shared ccb. mrsas_reset_ctrl's re-fire loop (mrsas.c:2448-2470) re-fires wrappers whose mfi cmd was already released by a timed-out waiter (no ownership check). The shared sleep ident sc->chan (mrsas.c:2880-2888, 3011-3012) additionally lets wakeup_one wake the wrong waiter. Not executable on the audit guest: mrsas is compiled into the kernel but no LSI HBA exists (no /dev/mrsas*), and manifestation additionally needs firmware that stalls a passthrough >180 s while a second ioctl recycles the command.
No comments yet.