OCR return-to-CAM flush races the lock-free ISR completion path: only a 1-second DELAY heuristic separates two unsynchronized mrsas_cmd_done callers (double xpt_done / duplicated free-list entry); timeout callout re-arms 600 s on recycled commands
Summary
mrsas_reset_ctrl flushes timed-out commands back to CAM with a non-atomic ccb_ptr check and mrsas_cmd_done call, while the MSI ISR's mrsas_complete_cmd does the same with NO common lock - its only guard is a test_bit checked before the walk. The fences are set_bit + disable_intr + a heuristic 1-second DELAY; disable_intr does not stop an already-delivered interrupt on another CPU. Overlap double-completes one mpt cmd: double xpt_done (CAM ccb UAF) or xpt_done(NULL) panic, plus double release duplicating the cmd on the free TAILQ. Aggravator: mrsas_scsiio_timeout unconditionally re-arms a 600 s callout that callout_stop cannot drain, so the stale timer later fires on the command's recycled owner and re-triggers OCR - converting a rare overlap into a recurring trigger after any single IO timeout. DF-3813 family. Atomic-claim idempotent-completion fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3845 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 4.1 KB | β raw | |
| run.log | β | 90 B | view raw | |
| env.txt | β | 260 B | view raw | |
| fix.diff | β | 3.1 KB | view raw | |
| manifest.json | β | 804 B | view raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-3845 β OCR return-to-CAM flush vs concurrent ISR completion (no serialization)
mrsas_reset_ctrl()'s return-to-CAM loop (mrsas.c:2335-2343) and the ISR completion path
(mrsas_isr β mrsas_complete_cmd, mrsas.c:1200/1236, lock-free) both test mpt_cmd->ccb_ptr
non-atomically and both call mrsas_cmd_done() for the same command when an in-flight
interrupt races the reset. The only fence is a 1-second DELAY heuristic (mrsas.c:2327) β
mrsas_disable_intr does not stop an already-delivered MSI on another CPU. Result:
double xpt_done (CAM ccb UAF), possible xpt_done(NULL) panic, and double
mrsas_release_mpt_cmd (duplicate free-list entry β two IOs sharing one mpt cmd).
mrsas_scsiio_timeout (mrsas_cam.c:370) unconditionally re-arms a 600 s callout that
callout_stop in mrsas_cmd_done cannot drain, so the stale timer fires on the command's
next owner and re-triggers OCR.
Reproduce
Race needs an attached MegaRAID HBA (completions in flight + induced fault/timeout to start
OCR). The audit guest has mrsas compiled in (kldstat -v β 80 pci/mrsas) but no LSI PCI
device and no /dev/mrsas* (run.log) β the code path cannot be entered.
Files
VERDICT.mdβ full interleaving + aggravator analysisrun.log,env.txtβ guest presence checksfix.diffβ idempotent completion (atomic cmpset claim), no 600 s re-arm, reset-loop claimmanifest.json,verdict.json
VERDICT β DF-3845 (OCR return-to-CAM flush vs concurrent ISR completion; callout re-arm on recycled cmd)
status: inconclusive (hardware-gated; race structure source-certain, overlap window timing-dependent) confidence: likely Β· class: missing serialization between reset path and completion path (DF-3813 family)
The race (source)
Sides that complete the same mrsas_mpt_cmd with no common lock:
Side A β ISR: mrsas_isr (INTR_MPSAFE, mrsas.c:1200-1223) β mrsas_complete_cmd
(mrsas.c:1236-1344) runs with no lock held; for LD/Fast-Path completions it calls
mrsas_map_mpt_cmd_status (writes cmd->ccb_ptr->ccb_h.status, mrsas.c:1353-1391) and then
mrsas_cmd_done (mrsas_cam.c:1080-1089): callout_stop, mrsas_unmap_request, xpt_done(ccb),
ccb_ptr = NULL, mrsas_release_mpt_cmd (TAILQ insert, mrsas_cam.c:605-615).
Its only reset guard is test_bit(MRSAS_FUSION_IN_RESET, &sc->reset_flags) at mrsas.c:1213 β
checked before the completion walk, with no memory barrier against the reset thread.
Side B β OCR thread: mrsas_ocr_thread (mrsas.c:2232-2273, holds recursive sim_lock) β
mrsas_reset_ctrl (mrsas.c:2309-2498):
1. set_bit(MRSAS_FUSION_IN_RESET) at mrsas.c:2324,
2. mrsas_disable_intr at mrsas.c:2326 β but an MSI/line interrupt already delivered to
another CPU still runs its handler,
3. DELAY(1000*1000) at mrsas.c:2327 β a 1-second heuristic that is the only fence,
4. mrsas_wait_for_outstanding (mrsas.c:2522) and, if commands remain, the return-to-CAM loop
at mrsas.c:2335-2343: if (mpt_cmd->ccb_ptr) { β¦ mrsas_cmd_done(sc, mpt_cmd); β¦ }.
If an ISR entered mrsas_complete_cmd before step 1 and is still executing when the loop at
step 4 runs (possible under a deep reply-queue drain with max_fw_cmds completions and slow
xpt_done under heavy CAM load β the walk is unbounded until it meets an unused descriptor),
both sides pass their non-atomic ccb_ptr check for the same cmd and both call
mrsas_cmd_done:
- double
xpt_done(ccb)β CAM completes a ccb twice β use-after-free in the CAM layer, xpt_done(cmd->ccb_ptr)racingcmd->ccb_ptr = NULLβ possiblexpt_done(NULL)panic,- double
mrsas_release_mpt_cmdβ the cmd is inserted twice intomrsas_mpt_cmd_list_head(mrsas_cam.c:611) β two future IOs share one mpt cmd/frame.
Aggravator β timeout callout: mrsas_scsiio_timeout (mrsas_cam.c:352-375) re-arms itself
for 600 s unconditionally after firing, and mrsas_cmd_done uses callout_stop (not
callout_drain/callout_stop_sync, mrsas_cam.c:1082), so a callback already running on
another CPU re-arms the callout on a command that completes and is recycled, firing 600 s
later on an unrelated IO (cmd->ccb_ptr of the new owner is non-NULL) and setting
do_timedout_reset β spurious OCR β the Side-A/Side-B race above, repeated on demand. This
converts a rare coincidence into a recurring trigger once any IO has timed out once.
Why not reproduced on the guest
Needs a MegaRAID HBA generating completions plus an induced FW fault/IO timeout to start OCR.
Guest has the driver compiled in but no LSI device and no /dev/mrsas* (run.log); CAM cannot
even queue IOs to the SIM. Overlap probability is timing-dependent even on hardware.
Hence inconclusive / likely.
Fix
fix.diff makes completion idempotent: mrsas_cmd_done claims the command via an atomic
compare-and-set of ccb_ptr (first claimer wins; the loser returns without touching the ccb),
and the reset loop uses the same claim. This closes the double-completion endpoint regardless
of the delay heuristic; the diff also replaces the dummy 600 s re-arm in the timeout callback
with a plain return when a reset is already flagged.
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 makes completion idempotent (atomic cmpset of ccb_ptr) so no interleaving can double-complete, and removes the 600 s re-arm. Not boot-testable on this guest (no HBA) -> not_testable.
fix.diff hunks in mrsas_cam.c (timeout callback, cmd_done) and mrsas.c reset flush loop.
Confirmed kernel references
- sys/dev/raid/mrsas/mrsas.c:1213
- sys/dev/raid/mrsas/mrsas.c:1219
- sys/dev/raid/mrsas/mrsas.c:2324
- sys/dev/raid/mrsas/mrsas.c:2326
- sys/dev/raid/mrsas/mrsas.c:2327
- sys/dev/raid/mrsas/mrsas.c:2335
- sys/dev/raid/mrsas/mrsas.c:2340
- sys/dev/raid/mrsas/mrsas.c:2522
- sys/dev/raid/mrsas/mrsas_cam.c:352
- sys/dev/raid/mrsas/mrsas_cam.c:370
- sys/dev/raid/mrsas/mrsas_cam.c:1080
- sys/dev/raid/mrsas/mrsas_cam.c:1082
- sys/dev/raid/mrsas/mrsas_cam.c:611
Detail
Exploit chain
induce one IO timeout (slow/stuck firmware) -> timeout callback sets do_timedout_reset and re-arms on the cmd -> OCR thread resets while an ISR drain is in flight -> both sides complete the same mpt cmds -> duplicated free-list entries / double xpt_done -> pool corruption and CAM-layer use-after-free; recurring because the 600 s re-arm fires on recycled commands.
Evidence (decisive lines)
VERDICT.md interleaving with mrsas.c/mrsas_cam.c line cites; run.log guest presence check (driver in kernel, no HBA, no /dev/mrsas*).
PoC changes
No seed PoC; race needs an attached HBA generating completions plus an induced fault/timeout. Guest cannot reach the code (no /dev/mrsas*, no SIM activity).
Verified recommended fix
Make mrsas_cmd_done idempotent via an atomic cmpset claim of ccb_ptr, stop the unconditional 600 s callout re-arm in mrsas_scsiio_timeout, and have the OCR flush loop rely on the same claim.
Verdict
Missing serialization between the OCR flush and the ISR completion path (DF-3813 concurrent-reinit family). mrsas_complete_cmd runs from the MSI handler with no lock (mrsas.c:1219, guard only the pre-walk test_bit at mrsas.c:1213); mrsas_reset_ctrl's return-to-CAM loop (mrsas.c:2335-2343) checks mpt_cmd->ccb_ptr and calls mrsas_cmd_done with only a 1-second DELAY heuristic (mrsas.c:2327) protecting against an already-running ISR β mrsas_disable_intr (mrsas.c:2326) does not stop a delivered interrupt on another CPU. When both sides pass their non-atomic check, one mpt cmd gets double mrsas_cmd_done: double xpt_done (CAM ccb UAF) or xpt_done(NULL) panic, plus double mrsas_release_mpt_cmd duplicating the cmd on the free TAILQ (mrsas_cam.c:611) so two future IOs share one command. Aggravator: mrsas_scsiio_timeout unconditionally re-arms a 600 s callout (mrsas_cam.c:370) and mrsas_cmd_done's callout_stop (mrsas_cam.c:1082) does not drain a running callback, so the timer fires later on the recycled command and re-triggers OCR β turning a one-in-a-million overlap into a recurring trigger after any single IO timeout. Cannot execute on the audit guest: mrsas is compiled in but no LSI HBA exists, so no completions/OCR can occur (run.log).
No comments yet.