mps_request_polled has no timeout cancellation: mps_update_events prints a NULL reply (NULL-deref panic) and callers free still-posted commands that late-complete against freed/reallocated cm state
Summary
After 1000x50ms without a reply, mps_request_polled returns ETIMEDOUT with the request still posted. (i) mps_update_events then calls mps_print_event(sc, reply) with reply==NULL (cm_reply zeroed) and the print derefs EventDataLength immediately - NULL-deref panic at attach/event re-registration. (ii) Both polled callers free the still-live command; a late reply hits mps_intr_locked and completes a free-list/REALLOCATED cm: spurious wakeup for the new owner, stale cm_reply recycled - reply-frame double-recycle and command-state corruption. Same trust boundary as DF-1369/4014 but distinct code and shape. NULL-guard + mark-timed-out fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4075 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| verdict.json | β | 2.4 KB | view raw | |
| README.md | β | 297 B | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| fix.diff | β | 987 B | view raw | |
| manifest.json | β | 755 B | view raw |
DF-4075 β mps_request_polled timeout lifecycle: NULL-deref panic + freed-command late completion
File: sys/dev/raid/mps/mps.c:2140-2161 (polled loop), :1720-1727 (NULL print), :592-596 (free on error) Source-level finding; no mps controller on the audit guest (not runnable). See VERDICT.md.
DF-4075 VERDICT β source-level certain, chip-gated (no LSI mps HBA on audit guest)
Shape (i): NULL-deref panic
- mps_update_events():1719
error = mps_request_polled(sc, cm); :1720-1723 set error when reply is NULL / bad status; :1724 then callsmps_print_event(sc, reply)UNCONDITIONALLY. - On a polled timeout (mps_request_polled:2150-2157: 1000 iterations x 50 ms = 50 s with no reply), cm->cm_reply is NULL (never assigned; also zeroed by every prior mps_free_command β mpsvar.h:520).
- mps_print_event() (mps_table.c) dereferences
event->EventDataLengthimmediately β NULL deref β panic. Reachable at attach (mps_attach_log -> mps_register_events -> mps_update_events) and on every event re-registration, whenever the IOC stays silent ~50 s without faulting.
Shape (ii): timed-out command freed while still posted
- mps_request_polled() returns ETIMEDOUT with the request still posted to the controller (no cancellation, no MPS_CM_FLAGS_COMPLETE).
- Callers free immediately: mps_get_portfacts
done:(:592-594 frees even on error), mps_update_events (:1727). - A late reply then hits mps_intr_locked:1581
cm = &sc->commands[desc->AddressReply.SMID](SMID valid β DF-1369 covers invalid SMIDs): the cm is on the free list or REALLOCATED to a new request β spurious MPS_CM_FLAGS_COMPLETE / wakeup for the new owner, cm_reply assigned onto a free-list command, and mps_free_command later recycles the stale reply busaddr via mps_free_reply (mpsvar.h:518-519) β reply-frame double-recycle / command-state corruption.
Distinct from knowns
DF-4014 = mps_user.c ioctl wait path (frees cm + DMA buffer, device DMA writeback). DF-4015 = ioctl command leak. DF-1369 = SMID bounds. This finding is the mps.c core polled path.
Fix
NULL-guard the print; on timeout do not free a still-posted command (complete-with-error or defer reclamation to reinit).
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_testableHardware-gated finding (no AMD VegaM GPU / no LSI mps controller on the audit guest): fix authored at source level (fix.diff) but not build/boot-validated.
fix.diff in this pack; authored post-analysis, not hardware-validated.
Confirmed kernel references
Detail
Evidence (decisive lines)
mps.c:2150-2157 timeout loop leaves cm live; :1720-1724 NULL print; :592-596 and :1727 free-on-timeout; mpsvar.h:514-540 free semantics
PoC changes
n/a (source-level; no mps controller on the guest)
Verified recommended fix
NULL-guard the mps_print_event call; on polled timeout do not let callers free a still-posted command (complete it with error / mark and reclaim on reinit).
Verdict
Source-level certain, chip-gated (needs an mps-attached LSI IOC that stops answering). mps_request_polled() has no timeout cancellation: after 1000x50ms it returns ETIMEDOUT while the command is still posted to the controller. (i) mps_update_events():1719-1724 then calls mps_print_event(sc, reply) with reply == cm->cm_reply == NULL (mps_free_command zeroes it, mpsvar.h:520) and mps_print_event() dereferences event->EventDataLength unconditionally (mps_table.c) => NULL-deref panic. (ii) Both polled callers free the still-posted command on the error path (mps_get_portfacts:592-594 'done:', mps_update_events:1727); a late reply then lands on a freed/reallocated cm via mps_intr_locked:1581 -> spurious completion/wakeup of the new owner and a stale reply busaddr recycled by mps_free_reply (mpsvar.h:518-519) => reply-frame double-recycle and command-state corruption. Distinct from DF-4014 (user-ioctl wait path / DMA-buffer UAF) and DF-1369 (SMID validation).
No comments yet.