Stale completion after bus-reset recovery: trm_Interrupt dereferences NULL pActiveDCB/pActiveSRB (KASSERT only guards INVARIANTS), and trm_SRBdone's going-list unlink walk has no termination
Summary
Sibling paths of known DF-2524. INT_BUSSERVICE|INT_CMDDONE with pActiveDCB==NULL (cleared at three sites while interrupts stay enabled) reaches pSRB = pDCB->pActiveSRB - on GENERIC (KASSERT compiled out) a NULL-page read panic. Independently, trm_RecoverSRB empties the going list without clearing pDCB->pActiveSRB, so a stale completion routed through disc1 -> trm_SRBdone walks the unlink loop off the list end -> NULL deref. Hostile or flaky SCSI target: bus reset with tagged commands in flight + stale chip interrupt -> kernel panic (local DoS). Timing-dependent, hence speculative.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3833 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.2 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| verdict.json | β | 3.1 KB | view raw | |
| fix.diff | β | 1.3 KB | view raw | |
| env.txt | β | 1.1 KB | view raw | |
| manifest.json | β | 676 B | view raw |
DF-3833 β stale completion after bus-reset recovery: trm_Interrupt derefs NULL pActiveDCB/pActiveSRB; trm_SRBdone walks the going list without termination
- File:
sys/dev/disk/trm/trm.c(pass 2) - Class: CWE-476 NULL deref / CWE-835 unbounded walk β kernel panic (DoS), hardware-timing-gated
- Severity: Low, speculative (needs a stale chip interrupt in a specific window; sibling path of the known DF-2524 which was trm_Disconnect's pActiveSRB)
Build / Run / Expected
Hardware-gated (env.txt: no Tekram HBA on the audit guest). Trigger on hardware: issue
camcontrol reset / bus reset (XPT_RESET_BUS) or provoke an external bus reset while tagged
commands are in flight; the chip raises INT_BUSSERVICE/INT_CMDDONE for an aborted command
after the driver already cleared pACB->pActiveDCB.
Root cause
trm_Interrupt, trm.c:1337-1345: pDCB = pACB->pActiveDCB; KASSERT(pDCB != NULL, ("no active DCB")); pSRB = pDCB->pActiveSRB; <- also unchecked against NULL Windows wherepActiveDCBis NULL while the chip can still signal INT_BUSSERVICE|INT_CMDDONE:trm_DisconnectsetspACB->pActiveDCB = NULL(trm.c:2359) and the next command is only started later bytrm_DoWaitingSRB;trm_resetβtrm_DoingSRB_Donecompletes/frees all going SRBs and clearspActiveDCB(trm.c:1184-1185) before re-enabling interrupts (1176-1178) and starting new commands;trm_ScsiRstDetectclearspActiveDCB(trm.c:2805). On a GENERIC kernel (no INVARIANTS β KASSERT compiles to nothing, sys/sys/systm.h:117) a stale interrupt in that window panics atpDCB->pActiveSRB(NULL+offset read); on an INVARIANTS kernel the KASSERT fires β either way, panic.
trm_SRBdone, trm.c:2718-2729: if the completing SRB is not the list head, the walk while (psrb->pNextSRB != pSRB) psrb = psrb->pNextSRB; has no termination.trm_RecoverSRB(trm.c:1141-1160) moves every going SRB to the waiting list on external-reset detection, but does NOT clearpDCB->pActiveSRB; a stale completion routed throughtrm_Disconnectβdisc1βtrm_SRBdonethen walks off the end of the (now empty or reordered) going list and NULL-derefspsrb->pNextSRB.
VERDICT β DF-3833
status: inconclusive (speculative, hardware-gated) β reproduced: 0 β guest impact: none.
Window analysis
- trm_Interrupt BUSSERVICE/CMDDONE path (trm.c:1337-1350).
pACB->pActiveDCBis: - cleared at trm.c:2359 intrm_Disconnect(every disconnect, beforetrm_DoWaitingSRBstarts the next command); - cleared at trm.c:1185 intrm_resetaftertrm_DoingSRB_Donefreed all going SRBs and after interrupts were re-enabled at trm.c:1176-1178 (EN_SCSIINTR at 3369 keeps bus-service interrupts on); - cleared at trm.c:2805 intrm_ScsiRstDetect(external reset). In any of these windows an INT_BUSSERVICE/INT_CMDDONE from the aborted command reachespSRB = pDCB->pActiveSRBwithpDCB == NULL: on X86_64_GENERIC (KASSERT compiled out, sys/sys/systm.h:117) that is a NULL-page read β panic; on INVARIANTS the KASSERT itself panics. NotepDCB->pActiveSRBis also never NULL-checked even when pDCB is valid βpActiveSRBis NULL betweendisc1(trm.c:2402) and the next start. - trm_SRBdone unlink walk (trm.c:2718-2729).
while (psrb->pNextSRB != pSRB)assumes the SRB is on the going list.trm_RecoverSRB(trm.c:1141-1160) moves all going SRBs of every DCB to their waiting lists on external-reset detection β but leavespDCB->pActiveSRBpointing at the (now waiting-list) SRB. A subsequent stale completion routed throughtrm_Disconnectβdisc1βtrm_SRBdone(trm.c:2397-2404) then walks past the emptied list and derefspsrb->pNextSRBwithpsrb == NULL.
Sibling of known DF-2524 (trm_Disconnect's own pActiveSRB deref) β this finding covers the two other members of the same stale-completion family that pass 1 did not file.
Why not reproduced
No Tekram adapter on the audit guest (env.txt); the trigger additionally requires chip-level timing (stale interrupt between reset recovery and next selection). Confidence: speculative (reachability timing-dependent), the missing checks are certain in source.
Fix
fix.diff: drop stale BUSSERVICE/CMDDONE interrupts when pActiveDCB/pActiveSRB is NULL, and
make the going-list walk terminating (psrb != NULL guard + skip-unlink path).
git apply --check passes.
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_testableNeeds Tekram hardware plus reset/interrupt racing; absent on the audit guest. fix.diff is git-apply-able (verified).
fix.diff
Confirmed kernel references
Detail
Exploit chain
hostile/flaky SCSI target: keep a tagged command in flight -> force bus reset (or wait for external reset) -> chip raises stale bus-service interrupt before the next StartSCSI re-arms pActiveDCB -> kernel panic (local DoS). No privilege gain.
Evidence (decisive lines)
README.md (both windows with line refs and the RecoverSRB non-cleanup); env.txt; fix.diff (NULL guards + terminating walk, git-apply --check passes)
PoC changes
No PoC β requires Tekram hardware and reset/interrupt racing; analysis + fix only.
Verified recommended fix
NULL-guard pActiveDCB/pActiveSRB in trm_Interrupt before the phase dispatch and terminate the trm_SRBdone going-list walk (fix.diff).
Verdict
Two sibling panic windows around the reset paths that pass-1's DF-2524 (trm_Disconnect pActiveSRB NULL) did not cover. (1) trm_Interrupt (sys/dev/disk/trm/trm.c:1337-1343): INT_BUSSERVICE|INT_CMDDONE dereferences pACB->pActiveDCB under a KASSERT and then pDCB->pActiveSRB with no NULL check; pActiveDCB is NULL in real windows β trm_Disconnect clears it at trm.c:2359 before the next start, trm_reset clears it at trm.c:1185 after completing all going SRBs but after re-enabling interrupts (trm.c:1176-1178), and trm_ScsiRstDetect at trm.c:2805 β so a stale chip completion in those windows panics on GENERIC (KASSERT is INVARIANTS-only, sys/sys/systm.h:94-118) via the NULL pDCB->pActiveSRB read. (2) trm_SRBdone (trm.c:2720-2724) unlinks the completed SRB with an unbounded 'while (psrb->pNextSRB != pSRB)' walk; trm_RecoverSRB (trm.c:1141-1160) empties the going list on external-reset detection without clearing pDCB->pActiveSRB, so a stale completion of a recovered SRB walks off the list end and NULL-derefs. Both need adversarial chip timing (bus reset mid-command + stale interrupt) β plausible with flaky or hostile targets, not demonstrable on the audit guest (no Tekram HBA, env.txt). Speculative-likely, hardware-gated panic/DoS.
No comments yet.