mpt_scsi_reply_handler / mpt_scsi_tgt_reply_handler dereference req with no NULL check while mpt_intr dispatches handlers with req=NULL on out-of-range IOC context
Summary
mpt_intr() looks up the request from IOC-owned context and, when req_index >= MPT_MAX_REQUESTS, only WARNs - then calls the handler anyway with req=NULL. mpt_scsi_reply_handler immediately evaluates req->state and mpt_scsi_tgt_reply_handler computes MPT_TGT_STATE(mpt, req) before any check, while the sibling mpt_sata_pass_reply_handler in the same file defends with 'if (req != NULL)' - the asymmetry proves the omission. A faulty/malicious IOC (or corrupted reply DMA) yields a NULL-deref panic. Shared with upstream FreeBSD head (verified). IOC-firmware-defect/malicious-hardware class: deterministic panic on a single crafted reply.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3674 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 438 B | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| fix.diff | β | 744 B | view raw | |
| env.txt | β | 281 B | view raw | |
| verdict.json | β | 2.2 KB | view raw | |
| manifest.json | β | 633 B | view raw |
DF-3674 β NULL request dereference in mpt_scsi_reply_handler / mpt_scsi_tgt_reply_handler
Severity: Low (IOC-driven kernel NULL deref / panic). No userland trigger exists β the NULL req comes from an IOC reply whose context index exceeds the request pool (mpt.c:812-820 dispatches anyway). See VERDICT.md.
Build/run: none applicable (interrupt-path bug); guest has no MPT HBA (env.txt). Upstream FreeBSD shares the missing guard.
DF-3674 β mpt_scsi_reply_handler / mpt_scsi_tgt_reply_handler dereference req with no NULL check
Severity: Low β IOC-triggered NULL deref (panic); source-certain; runtime-gated on MPT hardware.
Root cause
mpt_intr() looks the request up from the IOC-supplied context and, when
the index is out of pool range, only WARNs β then dispatches the handler
with req == NULL:
/* sys/dev/disk/mpt/mpt.c:812-820 */
req_index = MPT_CONTEXT_TO_REQI(ctxt_idx);
if (req_index < MPT_MAX_REQUESTS(mpt)) {
req = &mpt->request_pool[req_index];
} else {
mpt_prt(mpt, "WARN: mpt_intr index == %d (reply_desc =="
" 0x%x)\n", req_index, reply_desc);
}
...
free_rf = mpt_reply_handlers[cb_index](mpt, req, ...);
ctxt_idx comes from IOC-owned memory: reply_frame->MsgContext for
address replies (mpt.c:744) or the masked context word for
MPI_CONTEXT_REPLY_TYPE_SCSI_INIT context replies (mpt.c:752-755).
The SCSI I/O handler then dereferences req before any validation:
/* sys/dev/disk/mpt/mpt_cam.c:2649 */
if (req->state == REQ_STATE_FREE) { /* NULL deref */
and the target handler likewise:
/* sys/dev/disk/mpt/mpt_cam.c:5204-5208 */
if (reply_frame == NULL) {
mpt_tgt_state_t *tgt = MPT_TGT_STATE(mpt, req); /* deref */
The sibling handler in the same file does defend:
/* sys/dev/disk/mpt/mpt_cam.c:874-878 */
mpt_sata_pass_reply_handler(...)
{
if (req != NULL) { /* <-- the missing pattern */
MPT_CONTEXT_TO_REQI masks a 24-bit IOC-controlled field, so any IOC reply
with a context whose request index is β₯ 512 (MPT_MAX_REQUESTS) while the
handler byte selects scsi_io/tgt produces a NULL-deref panic. That is a
firmware defect / malicious-IOC / bus-DMA-corruption scenario, identical in
threat class to the pass-1 findings DF-1161/DF-2537 but with a weaker
(NULL) primitive.
Upstream FreeBSD head has the same missing guard (verified) β a shared bug worth fixing in both trees.
Fix
--- a/sys/dev/disk/mpt/mpt_cam.c
+++ b/sys/dev/disk/mpt/mpt_cam.c
@@ -2646,6 +2646,11 @@ mpt_scsi_reply_handler(struct mpt_softc *mpt, request_t *req,
MSG_SCSI_IO_REQUEST *scsi_req;
union ccb *ccb;
+ if (req == NULL) {
+ mpt_prt(mpt, "mpt_scsi_reply_handler: NULL request\n");
+ return (TRUE);
+ }
+
if (req->state == REQ_STATE_FREE) {
mpt_prt(mpt, "mpt_scsi_reply_handler: req already free\n");
return (TRUE);
@@ -5201,6 +5206,12 @@ mpt_scsi_tgt_reply_handler(struct mpt_softc *mpt, request_t *req,
U16 status;
+ if (req == NULL) {
+ mpt_prt(mpt, "mpt_scsi_tgt_reply_handler: NULL request\n");
+ return (TRUE);
+ }
+
if (reply_frame == NULL) {
(kept verbatim in fix.diff.)
Guest verdict
inconclusive β mpt compiled into X86_64_GENERIC (line 93) but zero LSI PCI
devices exist on the QEMU guest, so no IOC replies are ever dispatched.
Source-level certainty: certain (dispatch-with-NULL is provable at
mpt.c:812-820).
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_testableGuard-only diff authored (fix.diff); cannot be exercised without MPT hardware on the guest.
fix.diff; env.txt
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (path:line proof incl. the sibling-handler contrast); fix.diff (guards mirroring the sata_pass handler); env.txt (no MPT hardware)
PoC changes
n/a (no PoC β interrupt-path, IOC-gated)
Verified recommended fix
Add 'if (req == NULL) return (TRUE);' guards to mpt_scsi_reply_handler and mpt_scsi_tgt_reply_handler, mirroring mpt_sata_pass_reply_handler
Verdict
Source-certain NULL-deref: mpt_intr() dispatches reply handlers with req=NULL when the IOC-supplied context index exceeds the 512-entry request pool (mpt.c:812-820 WARN path still calls the handler); mpt_scsi_reply_handler dereferences req->state immediately (mpt_cam.c:2649) and mpt_scsi_tgt_reply_handler computes MPT_TGT_STATE(mpt, req) before any NULL check (mpt_cam.c:5204-5208), while the sibling mpt_sata_pass_reply_handler defends with 'if (req != NULL)' (mpt_cam.c:878) β the asymmetry proves the omission. Trigger requires a faulty/malicious IOC or corrupted reply DMA (hardware trust gap), same threat class as pass-1 DF-1161/DF-2537 but weaker primitive (NULL deref -> panic, not OOB). Not executable on the audit guest: no LSI PCI devices, mpt never attaches. Upstream FreeBSD head has the same missing guards.
No comments yet.