Unbounded els_cmd_ptrs[] index from IOC-supplied TransactionContext in FC ELS reply handler
Summary
mpt_fc_els_reply_handler at mpt_cam.c:2951-2952: ioindex=le32toh(rp->TransactionContext) (U32 from IOC DMA) used as index into els_cmd_ptrs[MPT_MAX_ELS=64] with NO bounds check. mpt_intr validates MsgContext-derived ctxt_idx but NOT TransactionContext. OOB index -> arbitrary pointer treated as live request_t -> TAILQ_REMOVE with corrupt pointer, bitfield writes, memcpy through req_vbuf. FC target mode + LINK_SERVICE_BUFFER_POST_REPLY with MsgLength>5. Adjacent FC peer or IOC firmware defect. Same class as DF-1057/1122 family. Fix: validate ioindex<els_cmds_allocated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1161 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Add `if (ioindex >= mpt->els_cmds_allocated) return TRUE;` before els_cmd_ptrs[ioindex] access | 457 B | view raw |
| VERDICT.md | verdict | full mechanism trace + reachability note | 4.3 KB | β raw |
| README.md | readme | summary + reproduce steps | 1.4 KB | β raw |
| build.sh | build-script | apply fix + build mpt.ko | 374 B | view raw |
| run.sh | run-script | placeholder runtime trigger (needs FC HW) | 1.0 KB | view raw |
| build.log | build-log | full successful build of patched mpt.ko | 995 B | view raw |
| env.txt | environment | uname, cc, hw.model | 450 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1161 PoC verification β source-level trace of mpt_fc_els_reply_handler unbounded els_cmd_ptrs[] index bug.
The mpt(4) driver is compiled into the X86_64_GENERIC kernel
(sys/config/X86_64_GENERIC:93) but no LSI-Logic MPT-Fusion FC HBA is present
in the QEMU guest, so mpt->is_fc is never set and the ELS reply handler is
never dispatched. Verification is by source-level trace + build-validation of
the fix.
Bug location
sys/dev/disk/mpt/mpt_cam.c:2951βioindex = le32toh(rp->TransactionContext);sys/dev/disk/mpt/mpt_cam.c:2952βreq = mpt->els_cmd_ptrs[ioindex];(no bounds)els_cmd_ptrsallocated atmpt_cam.c:4208(64 ptrs = MPT_MAX_ELS)- Symmetric guard that exists for the SRVC_RSP path:
mpt_cam.c:2886-2892
Mechanism
IOC DMA-supplied U32 TransactionContext is used directly as an array index
into the 64-entry els_cmd_ptrs[] with no bounds check. A malicious or buggy
IOC reply with MsgLength > 5 and TransactionContext >= 64 reads an
attacker-influenced kernel pointer past the array and then dereferences it as a
live request_t * (TAILQ_REMOVE, bitfield writes, memcpy through req_vbuf).
Reproduce
- Apply
fix.diffto /usr/src. cd /usr/src/sys/dev/disk/mpt && make KMOD=mptβ mpt.ko builds clean.- Runtime test requires LSI-Logic MPT-Fusion FC HBA (not present on this guest).
See VERDICT.md for the full analysis.
DF-1161 β mpt_fc_els_reply_handler: unbounded els_cmd_ptrs[] index (source-only verification)
Verdict: REPRODUCED at source level (latent in compiled-in mpt(4); not triggerable without LSI-Logic MPT FC HBA)
Mechanism
mpt(4) is statically compiled into X86_64_GENERIC
(sys/config/X86_64_GENERIC:93). The Fiber Channel ELS reply handler runs only
when mpt->is_fc is true (set by the FC HBA attachment in mpt_pci.c).
When an FC LINK_SERVICE_BUFFER_POST_REPLY reply frame arrives with MsgLength >
5, the handler executes:
/* sys/dev/disk/mpt/mpt_cam.c:2951-2952 */
ioindex = le32toh(rp->TransactionContext); /* U32 from IOC DMA, no check */
req = mpt->els_cmd_ptrs[ioindex]; /* OOB if ioindex >= 64 */
els_cmd_ptrsis allocated asMPT_MAX_ELS * sizeof(request_t *)= 64 entries (sys/dev/disk/mpt/mpt_cam.c:4208, withMPT_MAX_ELS = 64atsys/dev/disk/mpt/mpt.h:373).TransactionContextis a U32 field of the IOC reply frame that the driver receives via DMA. The driver originally posts it from a known-good ioindex (mpt_cam.c:4149: tep->TransactionContext[0] = htole32(ioindex)), but the field is in IOC-owned memory between post and reply.- There is no bounds check on
ioindexbefore indexing. If an IOC firmware defect or a malicious FC peer (capable of crafting LINK_SERVICE_BUFFER_POST replies withMsgLength > 5and an arbitraryTransactionContext) suppliesioindex >= 64, the read fetches an attacker-controlled kernel-pointer from beyond the array. - The pointer is then dereferenced as a live
request_t *:TAILQ_REMOVEunlink (mpt_cam.c:2971,2985,3062,3072), bitfield writes (req->state &= ~REQ_STATE_QUEUED),memcpythroughreq_vbuf(mpt_cam.c:2943+ handler body), and re-post viampt_fc_post_els(mpt, req, ioindex)atmpt_cam.c:3075.
Effect: heap OOB read β arbitrary-pointer-as-request_t β memory corruption
and likely panic; in principle exploitable as a corrupt-pointer primitive by an
adjacent FC peer with a buggy/malicious IOC.
Note the asymmetric hardening that already exists: when the same ELS
reply arrives as MPI_FUNCTION_FC_LINK_SRVC_RSP (no TransactionContext
field available), the code at mpt_cam.c:2886-2892 walks the array linearly
and KASSERTs the index is in range. The BUF_POST path with
TransactionContext skips that safety walk and trusts the IOC-supplied index
directly β that is the gap.
The initiator-side analogue in mpt.c:765 correctly bounds
tgt_cmd_ptrs[ctxt_idx] against tgt_cmds_allocated. The els path has no
equivalent guard.
Trigger reachability on this guest
- No LSI-Logic MPT-Fusion FC HBA is present in the QEMU guest (
pciconf -lvshows none). The mpt(4) driver attaches to no FC device, sompt->is_fcis never set;mpt_fc_els_reply_handleris never dispatched. - The vulnerable code is in the running kernel (compiled-in) but is only reachable from an interrupt fired by FC HW.
Fix
fix.diff adds the symmetric bounds check before the array access:
ioindex = le32toh(rp->TransactionContext);
if (ioindex >= mpt->els_cmds_allocated) { /* mirrors mpt.c:765 */
mpt_prt(mpt, "ELS_REPLY: ioindex %u >= els_cmds_allocated %u\n",
ioindex, mpt->els_cmds_allocated);
return (TRUE);
}
req = mpt->els_cmd_ptrs[ioindex];
This mirrors the existing tgt_cmds_allocated guard in mpt.c:765 and uses
els_cmds_allocated (the actually-allocated count, set at mpt_cam.c:4238)
rather than the compile-time MPT_MAX_ELS upper bound.
Build verification of the fix
mpt.ko built successfully from the patched source on the guest
(cc 8.3 [DragonFly]):
--- mpt_cam.o --- cc -O2 -pipe ... -Werror -DKLD_MODULE ... -c mpt_cam.c --- mpt.ko --- cc -Wl,--build-id=sha1 -nostdlib -Wl,--hash-style=sysv -r -Wl,-d -o mpt.ko mpt.o mpt_cam.o mpt_debug.o mpt_pci.o mpt_raid.o mpt_user.o BUILD_EXIT=0
Full output: build.log. The fix is build-clean.
Fix-validation status
not_testable β the bug requires an LSI-Logic MPT-Fusion FC HBA (or an
emulator that fabricates IOC replies). The guest has neither. We confirmed
the fix applies cleanly and the patched module compiles; the change
is a 5-line bounds check identical in form to the existing
tgt_cmds_allocated guard in the same driver family.
Fix verification
not_testablecompile validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. mpt els_cmd_ptrs[ioindex] no bounds check -> OOB heap ptr read. mpt in GENERIC, no FC HBA.
No comments yet.