β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1162

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1162 Β· 9 files
FileTypeDescriptionSize
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
README.md readme summary + reproduce steps
↓ download 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

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

  1. Apply fix.diff to /usr/src.
  2. cd /usr/src/sys/dev/disk/mpt && make KMOD=mpt β†’ mpt.ko builds clean.
  3. 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.

VERDICT.md verdict full mechanism trace + reachability note
↓ download raw

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_tgtcmds is an alias for port_facts[0].MaxPostedCmdBuffers (sys/dev/disk/mpt/mpt.h:566) β€” a 16-bit IOC facts field.
  • MPT_MAX_REQUESTS/2 is the per-controller request pool halved.
  • When MaxPostedCmdBuffers > MPT_MAX_REQUESTS/2, tgt_cmd_ptrs is capped at MPT_MAX_REQUESTS/2 but the bound check still allows up to mpt_max_tgtcmds. Any ABTS with RX_ID in 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_fc is never set and the ABTS branch (rctl == ABTS && type == 0) of mpt_fc_els_reply_handler is 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_testable

compile 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.