Unguarded tws_reset re-entry: unlocked state check races the TWS_RESET_START transition β KASSERT panic (INVARIANTS) or leaked xpt_freeze_simq count freezing the adapter forever (twa sibling DF-4336)
Summary
The reset guards re-entry with an unlocked read but transitions under gen_lock with a KASSERT single-entry. Three concurrent callers (ccb timeout callouts, attn-error interrupt, passthru ETIMEDOUT). Double entry: panic on INVARIANTS kernels; on production the second freeze is never matched by reinit's single release - SIM queue frozen forever. Plus duplicated resets and reinit static retry counters shared across adapters. Lock+re-check fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4401 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 2.4 KB | β raw | |
| trigger.c | β | 833 B | view raw | |
| fix.diff | β | 2.1 KB | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| env.txt | β | 541 B | view raw |
DF-4401 β Unguarded tws_reset() re-entry: check-then-act race corrupts the reset state machine (panic / frozen simq)
What
tws_reset() (sys/dev/raid/tws/tws_cam.c:1197-1218) guards re-entry with an
unlocked read:
if ( tws_get_state(sc) == TWS_RESET ) {
return;
}
...
lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
tws_send_event(sc, TWS_RESET_START); /* KASSERT(state != TWS_RESET), tws.c:666 */
lockmgr(&sc->gen_lock, LK_RELEASE);
Three independent callers can run it concurrently:
- tws_timeout ccb-timeout callout (tws_cam.c:1185-1195) β one per queued I/O;
- tws_intr_attn_error microcontroller-error interrupt (tws_cam.c:1143-1153);
- tws_passthru ioctl thread on ETIMEDOUT (tws_user.c:143-147, holding
gen_lock β only survivable because it is LK_CANRECURSE, tws.c:199).
Two threads that both pass the unlocked check both execute the reset prologue:
- INVARIANTS kernels: the second
TWS_RESET_STARTtripsKASSERT(sc->tws_state != TWS_RESET)(tws.c:666) β panic (KASSERT/KKASSERTare compiled in only with INVARIANTS,sys/sys/systm.h:94-122); the secondTWS_RESET_COMPLETElater tripstws.c:674similarly. - Production kernels: duplicated
xpt_freeze_simq(sc->sim, 1)(tws_cam.c:1212-1214) with only ONE matchingxpt_release_simq(...,1)intws_reinit(:1268-1270) βqfrozen_cntleaks β₯1 (sys/bus/cam/cam_xpt.c:4852-4888) β the SIM queue stays frozen forever, all I/O on the adapter stalls permanently; plus a duplicated soft reset (tws_assert_soft_reset,:1216) and duplicatedreset_cb/reinitcallout chains. tws_reinit()(:1241-1281) keeps its retry counters (timeout_val,try) instaticstorage β shared across all adapters in the system.
Sibling: DF-4336 (twa tw_cl_reset_ctlr has no entry guard) β same defect class
carried into the tws CAM layer.
Impact
Local DoS (panic on debug kernels; permanent adapter I/O freeze on production kernels) triggered by controller distress that fires β₯2 of the reset callers near-simultaneously (e.g. two ccb timeouts racing a controller-error interrupt). No privilege needed beyond ordinary I/O to a distressed adapter.
Status
QEMU guest has no 3ware HBA β inconclusive; race proven from source.
VERDICT β DF-4401
Status: inconclusive (hardware-gated). The check-then-act race is certain from source; triggering needs the 3ware controller to be distressed enough that two reset callers overlap.
Proof chain
tws_resetentry guard is a plain read:tws_cam.c:1203-1205if (tws_get_state(sc) == TWS_RESET) return;β no lock held; the state transition happens later undergen_lockat:1207-1209.tws_send_event(TWS_RESET_START)asserts single-entry:tws.c:664-671KASSERT(sc->tws_state != TWS_RESET, ...);KASSERTpanics only on INVARIANTS kernels (sys/sys/systm.h:94-122).- Concurrent callers:
- ccb timeout callout per I/O:
tws_cam.c:807-808armstws_timeout, which callstws_reset(:1191-1194) for every timed-out request; - controller-error interrupt:tws_intr_attn_errorβtws_reset(:1143-1153); - passthrough ioctl timeout:tws_user.c:142-147βtws_resetwhile holdinggen_lock(recursive;tws.c:199). Nothing serializes them before the state check. - Production-kernel consequence of a double entry:
-
xpt_freeze_simq(sc->sim, 1)executed twice (tws_cam.c:1212-1214) vs a singlexpt_release_simq(sc->sim, 1)intws_reinit(:1268-1270):sendq.qfrozen_cntis a counter (sys/bus/cam/cam_xpt.c:4863-4865), so a leaked increment keeps the devq frozen permanently β every subsequenttws_execute_scsisees a frozen SIM; disk I/O on the adapter never resumes. - duplicatedtws_assert_soft_reset(:1216) and callout chains (:1217,:1238) β duplicated reinit sequences. - Multi-adapter defect:
tws_reinit'sstatic int timeout_val=0, try=2(:1246) is shared by every controller instance.
Impact ceiling
Panic (INVARIANTS) or permanent adapter-wide I/O hang (production) β a local denial of service; no info leak or privilege path. Rated Low consistent with DF-4336.
Fix
Serialize tws_reset entry under gen_lock with a re-check after acquisition
(fix.diff), and move tws_reinit's retry counters into the softc.
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 moves the state check under gen_lock and makes reinit counters per-adapter; not validatable on the guest (no controller).
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (race proof + freeze-count arithmetic); fix.diff (locked re-check + per-adapter counters); trigger.c (reproduction recipe)
PoC changes
no seed; recipe stub only
Verified recommended fix
Serialize tws_reset entry: take gen_lock, re-check TWS_RESET, then run the RESET_START transition; move tws_reinit retry counters into the softc.
Verdict
Certain from source, hardware-gated to trigger. tws_reset() guards re-entry with an unlocked state read (tws_cam.c:1203-1205) while the TWS_RESET transition happens later under gen_lock (:1207-1209) and tws_send_event asserts single entry (KASSERT at tws.c:666). Three concurrent callers exist (ccb-timeout callout :1191-1194 armed per I/O at :807; controller-error interrupt :1143-1153; passthrough ETIMEDOUT thread tws_user.c:146 holding gen_lock, survivable only via LK_CANRECURSE). Double entry: INVARIANTS kernels panic at tws.c:666/674; production kernels leak an xpt_freeze_simq count (double freeze at :1212-1214 vs single release in tws_reinit :1268-1270, counter semantics in sys/bus/cam/cam_xpt.c:4863-4865) leaving the SIM queue frozen forever = permanent adapter I/O hang, plus duplicated soft resets. tws_reinit also keeps retry counters in function-static storage shared across adapters (:1246). Guest lacks the HBA.
No comments yet.