tgt_cmd_ptrs[rx_id] bounds check uses wrong limit in FC ABTS path
Summary
mpt_fc_els_reply_handler ABTS path at mpt_cam.c:3004: rx_id checked against mpt_max_tgtcmds (IOC facts MaxPostedCmdBuffers) but tgt_cmd_ptrs allocated as min(MPT_MAX_REQUESTS/2=256, mpt_max_tgtcmds). If MaxPostedCmdBuffers>256: array smaller than bound. ABTS with RX_ID in [256,MaxPostedCmdBuffers) reads OOB pointer from heap. Adjacent FC peer sends ABTS with crafted RX_ID. Fix: use tgt_cmds_allocated as bound matching mpt.c:765.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1162 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Replace `rx_id >= mpt->mpt_max_tgtcmds` with `rx_id >= mpt->tgt_cmds_allocated` (matches mpt.c:765 + allocation size at mpt_cam.c:4283) | 616 B | view raw |
| VERDICT.md | verdict | full mechanism trace + reachability note | 3.7 KB | β raw |
| README.md | readme | summary + reproduce steps | 1.4 KB | β raw |
| build.sh | build-script | apply fix + build mpt.ko | 372 B | view raw |
| run.sh | run-script | placeholder runtime trigger (needs FC HW) | 1.1 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 | 437 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-1162 PoC verification β source-level trace of mpt_fc_els_reply_handler tgt_cmd_ptrs[rx_id] bound mismatch.
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 ABTS path of the ELS
reply handler is never entered. Verification is by source-level trace +
build-validation of the fix.
Bug location
sys/dev/disk/mpt/mpt_cam.c:3004βif (rx_id >= mpt->mpt_max_tgtcmds)(wrong bound)sys/dev/disk/mpt/mpt_cam.c:3009βtgt_req = mpt->tgt_cmd_ptrs[rx_id];(OOB)tgt_cmd_ptrsallocated atmpt_cam.c:4251-4256(capped at MPT_MAX_REQUESTS/2)- Correct bound already used at
mpt.c:765(tgt_cmds_allocated)
Mechanism
tgt_cmd_ptrs is allocated as min(MPT_MAX_REQUESTS/2, mpt_max_tgtcmds)
entries, but the ABTS RX_ID check uses mpt_max_tgtcmds. When
MaxPostedCmdBuffers > MPT_MAX_REQUESTS/2, the array is smaller than the
checked bound; an ABTS with RX_ID in [tgt_cmds_allocated, mpt_max_tgtcmds)
reads an OOB pointer.
Reproduce
- Apply
fix.diffto /usr/src. cd /usr/src/sys/dev/disk/mpt && make KMOD=mptβ mpt.ko builds clean.- Runtime test requires FC HBA with
MaxPostedCmdBuffers > MPT_MAX_REQUESTS/2+ adjacent FC peer sending crafted ABTS (not present on this guest).
See VERDICT.md for the full analysis.
DF-1162 β mpt_fc_els_reply_handler ABTS: tgt_cmd_ptrs[rx_id] bound mismatch (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 FC ABTS (Abort Sequence) handler runs
only when mpt->is_fc is true (set by the FC HBA attachment).
tgt_cmd_ptrs is allocated as max = min(MPT_MAX_REQUESTS/2,
mpt_max_tgtcmds) entries:
/* sys/dev/disk/mpt/mpt_cam.c:4251-4256 */
max = MPT_MAX_REQUESTS(mpt) >> 1;
if (max > mpt->mpt_max_tgtcmds) {
max = mpt->mpt_max_tgtcmds;
}
mpt->tgt_cmd_ptrs =
kmalloc(max * sizeof (request_t *), M_DEVBUF, M_NOWAIT | M_ZERO);
But the ABTS path bounds-checks rx_id against mpt_max_tgtcmds, not
against max / tgt_cmds_allocated:
/* sys/dev/disk/mpt/mpt_cam.c:3004-3010 */
if (rx_id >= mpt->mpt_max_tgtcmds) { /* wrong bound */
mpt_prt(mpt, "Bad RX_ID 0x%x\n", rx_id);
} else if (mpt->tgt_cmd_ptrs == NULL) {
mpt_prt(mpt, "No TGT CMD PTRS\n");
} else {
tgt_req = mpt->tgt_cmd_ptrs[rx_id]; /* OOB if max < rx_id < mpt_max_tgtcmds */
}
mpt_max_tgtcmdsis an alias forport_facts[0].MaxPostedCmdBuffers(sys/dev/disk/mpt/mpt.h:566) β a 16-bit IOC facts field.MPT_MAX_REQUESTS/2is the per-controller request pool halved.- When
MaxPostedCmdBuffers > MPT_MAX_REQUESTS/2,tgt_cmd_ptrsis capped atMPT_MAX_REQUESTS/2but the bound check still allows up tompt_max_tgtcmds. Any ABTS withRX_IDin the half-open interval[tgt_cmds_allocated, mpt_max_tgtcmds)passes the check and reads an OOB pointer from beyond the array.
Note the existing correct guard in mpt.c:765 for the analogous target
context-reply path: if (ctxt_idx >= mpt->tgt_cmds_allocated). The ABTS path
in mpt_cam.c:3004 does not use this correct bound.
Effect: heap OOB read β attacker-controlled pointer dereferenced as
request_t *tgt_req β mpt_abort_target_cmd(mpt, tgt_req)
(mpt_cam.c:3046), which then dereferences MPT_TGT_STATE(mpt, tgt_req)
fields. Crash or memory corruption.
Trigger reachability on this guest
- No LSI-Logic MPT-Fusion FC HBA is present in the QEMU guest. The mpt(4)
driver attaches to no FC device, so
mpt->is_fcis never set and the ABTS branch (rctl == ABTS && type == 0) ofmpt_fc_els_reply_handleris never entered.
Fix
fix.diff replaces the wrong bound with the allocation-correct one:
if (rx_id >= mpt->tgt_cmds_allocated) { /* matches mpt.c:765 + mpt_cam.c:4283 */
mpt_prt(mpt, "Bad RX_ID 0x%x (max %u)\n", rx_id,
mpt->tgt_cmds_allocated);
} else if (mpt->tgt_cmd_ptrs == NULL) {
...
This makes the ABTS check identical in form to the existing
tgt_cmds_allocated guard in mpt.c:765 and consistent with the allocation
size recorded at mpt_cam.c:4283.
Build verification of the fix
mpt.ko built successfully from the patched source on the guest:
--- mpt_cam.o --- cc -O2 -pipe ... -Werror -DKLD_MODULE ... -c mpt_cam.c --- mpt.ko --- cc -Wl,--build-id=sha1 -nostdlib ... -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 β requires an LSI-Logic MPT-Fusion FC HBA configured such that
MaxPostedCmdBuffers > MPT_MAX_REQUESTS/2, plus an adjacent FC peer sending
ABTS frames with crafted RX_ID. The guest has neither. We confirmed the fix
applies cleanly and the patched module compiles; the change is a
single-token correction (replacing one identifier with the allocation-correct
one already used in the same driver at mpt.c:765).
Fix verification
not_testablecompile validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. mpt tgt_cmd_ptrs[rx_id] wrong bound mpt_max_tgtcmds vs tgt_cmds_allocated. mpt in GENERIC, no FC HBA.
No comments yet.