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

Unchecked reply-frame offset in mpt_intr allows OOB read/write via malicious IOC address reply

Summary

mpt_intr address-reply path at mpt.c:738-739: reply_baddr=MPT_REPLY_BADDR(reply_desc), offset=reply_baddr-reply_phys. NO check offset<2*PAGE_SIZE. reply_frame=MPT_REPLY_OTOV(mpt,offset)=&m->reply[offset] (:743) -> le32toh(reply_frame->MsgContext) OOB read (:744). Event deferral path mpt.c:588-589: evtf->context=context OOB write. Malicious IOC posts crafted reply desc with out-of-range address. Fix: bounds-check offset before MPT_REPLY_OTOV.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1280 Β· 8 files
FileTypeDescriptionSize
trigger_analysis.c trigger-source documentation marker 703 B view raw
fix.diff suggested-fix bounds-check offset against 2*PAGE_SIZE before MPT_REPLY_OTOV in mpt_intr 1.0 KB view raw
build.sh build-script syntax-checks the marker 432 B view raw
run.sh run-script documents INCONCLUSIVE status 647 B view raw
combined_build.log build-log combined X86_64_GENERIC rebuild log; rc=0 5.6 MB ↓ download
env.txt environment uname, pciconf -l, kldstat -v 1015 B view raw
README.md readme human-readable summary 2.8 KB ↓ raw
VERDICT.md verdict detailed source-level analysis 3.8 KB ↓ raw
README.md readme human-readable summary
↓ download raw

DF-1280 β€” Unchecked reply-frame offset in mpt_intr (OOB read/write)

Finding

The address-reply path in mpt_intr (sys/dev/disk/mpt/mpt.c:709) at lines 738-744:

reply_baddr = MPT_REPLY_BADDR(reply_desc);                 /* 738 */
offset = reply_baddr - (mpt->reply_phys & 0xFFFFFFFF);     /* 739 */
bus_dmamap_sync_range(... offset, MPT_REPLY_SIZE, ...);     /* 740-742 */
reply_frame = MPT_REPLY_OTOV(mpt, offset);                 /* 743 */
ctxt_idx = le32toh(reply_frame->MsgContext);               /* 744 */

There is no bounds check on offset. The reply DMA pool is exactly 2 * PAGE_SIZE bytes (allocated in mpt_pci.c:723-738); MPT_REPLY_OTOV expands to &m->reply[offset] (mpt.h:842-843), so a reply_desc whose address bits encode an out-of-window reply_baddr drives both bus_dmamap_sync_range and the dereference at line 744 out of bounds.

The event-deferral fast path at lines 588-589 (evtf->context = context;) then writes back into the same out-of-range reply_frame, so the bug is an OOB read and an OOB write β€” into an attacker-influenced kernel heap offset.

A malicious or buggy IOC that posts a crafted reply descriptor (e.g. via PCIe DMA of a malicious reply queue entry) triggers this on the next interrupt. The MPI_ADDRESS_REPLY_A_BIT mask is just the LSB of the descriptor; the upper 31 bits are taken straight from the descriptor.

Fix

Bounds-check offset against the actual reply pool size (2 * PAGE_SIZE) before bus_dmamap_sync_range and MPT_REPLY_OTOV. If out of range, drop the reply (MPT_REPLY_EMPTY) and continue the loop, so the bad descriptor neither crashes the kernel nor feeds bogus MsgContext into the request-pool indexing downstream.

Verification on this guest

  • The mpt driver is statically compiled in but no MPT HBA exists in QEMU (see DF-1278 README), so mpt_intr is never invoked. Bug is not runtime-triggerable on this guest.
  • Source-level confirmation:
  • mpt_pci.c:724-727 β€” reply DMA tag maxsize is 2 * PAGE_SIZE.
  • mpt_pci.c:733-738 β€” bus_dmamem_alloc allocates that exact size.
  • mpt.h:840-843 β€” MPT_REPLY_BADDR(x) = (x << 1), MPT_REPLY_OTOV(m, i) = ((void *)(&m->reply[i])).
  • mpt.c:739 β€” offset = reply_baddr - reply_phys; (no validation).
  • mpt.c:743-744 β€” reply_frame = &m->reply[offset]; ctxt_idx = ...->MsgContext;
  • mpt.c:588-589 β€” event-deferral path writes evtf->context back through the same out-of-range pointer.
  • Fix verified to compile (combined build with DF-1278/1279/1285/1287).

Realistic impact ceiling

Hardware-attacker OOB read+write into the kernel heap via a crafted IOC address-reply descriptor. CVSS reflects C:H/I:H/A:H at AV:P because the write targets (function pointers, refcounts) are bucket-dependent but attacker-influenced through the MsgContext indexing path.

VERDICT.md verdict detailed source-level analysis
↓ download raw

VERDICT β€” DF-1280

Status

INCONCLUSIVE (source-confirmed; not runtime-triggerable on this guest).

Mechanism (source-confirmed)

The address-reply path in mpt_intr (sys/dev/disk/mpt/mpt.c:709) at lines 738-744:

reply_baddr  = MPT_REPLY_BADDR(reply_desc);              /* 738 */
offset       = reply_baddr - (mpt->reply_phys & 0xFFFFFFFF); /* 739 */
bus_dmamap_sync_range(mpt->reply_dmat, mpt->reply_dmap,
    offset, MPT_REPLY_SIZE, BUS_DMASYNC_POSTREAD);       /* 740-742 */
reply_frame  = MPT_REPLY_OTOV(mpt, offset);              /* 743 */
ctxt_idx     = le32toh(reply_frame->MsgContext);         /* 744 */

There is no bounds check on offset. The reply DMA pool is exactly 2 * PAGE_SIZE bytes (allocated in mpt_pci.c:724-738). MPT_REPLY_OTOV(m, i) expands to ((void *)(&m->reply[i])) (mpt.h:842-843), so a reply_desc whose address bits encode an out-of-window reply_baddr drives both bus_dmamap_sync_range and the dereference at line 744 out of bounds.

The event-deferral fast path at lines 588-589 writes back into the same out-of-range pointer:

evtf = (struct mpt_evtf_record *)reply_frame;
evtf->context = context;

So the bug is both an OOB read (line 744) and an OOB write (line 589), into an attacker-influenced kernel heap offset.

A malicious or buggy IOC posts a crafted reply descriptor via DMA into the host-visible reply queue; on the next interrupt this path dereferences it. The MPI_ADDRESS_REPLY_A_BIT is just the LSB of the descriptor; the upper 31 bits come straight from the descriptor.

Fix

Bounds-check offset against the reply pool size (2 * PAGE_SIZE) before bus_dmamap_sync_range and MPT_REPLY_OTOV. If out of range, drop the reply (MPT_REPLY_EMPTY) and continue the loop, so the bad descriptor neither crashes the kernel nor feeds bogus MsgContext into the request-pool indexing downstream.

Verification on this guest

  • mpt is statically compiled into X86_64_GENERIC (sys/config/X86_64_GENERIC:93 β€” device mpt).
  • No MPT HBA exists in the QEMU guest, so mpt_intr is never invoked. Not runtime-triggerable.
  • Source-level proof of the OOB:
  • mpt_pci.c:724-727 β€” reply DMA tag maxsize is 2 * PAGE_SIZE.
  • mpt_pci.c:733-738 β€” bus_dmamem_alloc of that exact size.
  • mpt.h:840-843 β€” MPT_REPLY_BADDR(x) = (x << 1); MPT_REPLY_OTOV(m, i) = ((void *)(&m->reply[i])).
  • mpt.c:739 β€” offset = reply_baddr - reply_phys; (no validation).
  • mpt.c:743-744 β€” reply_frame = &m->reply[offset]; …->MsgContext (OOB read).
  • mpt.c:588-589 β€” event-deferral path evtf->context = context; (OOB write).
  • Fix validation: combined X86_64_GENERIC rebuild with this fix applied exited rc=0. Full log: combined_build.log.

Exploit chain

None on this guest (no HW). On a system with a malicious MPT HBA, the primitive is OOB read+write into kernel heap from IRQ context at the offset the IOC chose. The realistic ceiling is determined by what slab object sits at the chosen offset in the 2*PAGE_SIZE pool's neighborhood β€” but no escalation is attempted here because the path is unreachable on this guest.

Realistic impact ceiling

Hardware-attacker kernel heap OOB read+write. CVSS: AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H β€” the write target is attacker-influenced through MsgContext-indexed request-pool lookups, so function-pointer / refcount corruption is conceivable in the right bucket.

PoC changes

Folder was empty; added trigger_analysis.c, build.sh, run.sh, README.md, this VERDICT.md, fix.diff, manifest.json, combined_build.log.

Matches the finding proposal: bounds-check offset before MPT_REPLY_OTOV. See fix.diff.

Fix status

not_testable β€” bug requires absent hardware. fix.diff applies cleanly and compiles into X86_64_GENERIC (combined build rc=0).

Fix verification

not_testable

compile validated

nativekernel rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. mpt_intr reply offset no bounds -> OOB read+write via reply pool. mpt in GENERIC, no MPT HW.