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

Unvalidated chip-reported IOCRequestFrameSize sizes every request slot; all request builders write fixed sizeof(struct) with no minimum check

Summary

facts->IOCRequestFrameSize (U16 from the doorbell IOC_FACTS reply) sizes the request pool and each per-command slot; the driver then writes full fixed-size MPI2 structs into slots with nothing validating a minimum (grep: only sizing arithmetic). A controller reporting < ~16 dwords gets adjacent-slot overwrites inside the req_frames DMA map, and the LAST slot writes past the mapping. Chip-gated heap OOB write. Validate-at-attach fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4076 Β· 5 files
FileTypeDescriptionSize
verdict.json β€” 2.3 KB view raw
README.md β€” 254 B ↓ raw
VERDICT.md β€” 1.8 KB ↓ raw
fix.diff β€” 396 B view raw
manifest.json β€” 768 B view raw

DF-4076 β€” mps: chip-reported IOCRequestFrameSize sizes request slots with no minimum validation

File: sys/dev/raid/mps/mps.c:750 (sizing), :851-854 (slots), builders :574-576, :1703-1714, :2183-2214 Source-level finding; chip-gated. See VERDICT.md.

VERDICT.md
↓ download raw

DF-4076 VERDICT β€” source-level likely, chip-gated

Root cause

  • facts->IOCRequestFrameSize (U16, mpi2_ioc.h:272) is doorbell-read at attach (mps_get_iocfacts, mps.c:1043) and used verbatim to size BOTH the request pool (:750 rsize = IOCRequestFrameSize * num_reqs * 4) and each per-command slot (:851-854 cm_req = req_frames + iIOCRequestFrameSize4).
  • The driver then writes FIXED-SIZE MPI2 request structs into those slots: mps_get_portfacts MPI2_PORT_FACTS_REQUEST (:574-576) mps_update_events/rereg MPI2_EVENT_NOTIFICATION_REQUEST ~56B incl. 16B EventMasks (:1703-1714, :1752-1763) mps_read_config_page MPI2_CONFIG_REQUEST + SGE (:2183-2214) (mps_user.c builds even larger user requests β€” knowns DF-1359 etc. cover user paths.)
  • No code validates IOCRequestFrameSize against any minimum: grep shows it is only used in sizing arithmetic and printed once (mps_table.c:221).

Consequence

A controller (malicious or broken firmware) reporting IOCRequestFrameSize < ~16 dwords makes every builder overwrite adjacent slots inside the req_frames DMA mapping; for the LAST slot (i = num_reqs-1) the write runs past the mapping β‡’ heap OOB write of chip-gated size. Confidence 'likely' only because a conforming MPI2 IOC always reports a much larger frame.

Fix

Validate at attach: reject/clamp IOCRequestFrameSize below the largest request the driver builds (e.g. < sizeof(MPI2_CONFIG_REQUEST)/4 or an explicit MPI2 minimum).

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_testable
baseline no→ patch + rebuild →patched clean

Hardware-gated finding (no AMD VegaM GPU / no LSI mps controller on the audit guest): fix authored at source level (fix.diff) but not build/boot-validated.

fix.diff in this pack; authored post-analysis, not hardware-validated.
↓ fix.diffper-fix-DF-4076

Confirmed kernel references

Detail

Evidence (decisive lines)

grep IOCRequestFrameSize: used for sizing at mps.c:750/:772/:820-823/:851-854, echoed to sysctl/log only (mps_table.c:221), never validated

PoC changes

n/a (source-level)

Verified recommended fix

Validate facts->IOCRequestFrameSize at attach (reject/clamp below the largest request struct the driver builds, e.g. < sizeof(MPI2_CONFIG_REQUEST)/4).

Verdict

Source-level likely, chip-gated. The per-command request-frame slot size is facts->IOCRequestFrameSize*4 (mps.c:750,851-854), taken verbatim from the controller's IOC facts reply (U16, doorbell-read at :1043) and never validated against the largest MPI2 request the driver builds. mps_update_events() writes a full MPI2_EVENT_NOTIFICATION_REQUEST (~56B incl. 16B EventMasks) at :1703-1714, mps_read_config_page() a full MPI2_CONFIG_REQUEST + SGE at :2183-2214, mps_get_portfacts() at :574-576. A controller reporting IOCRequestFrameSize < ~16 dwords makes every builder overwrite adjacent slots inside the req_frames DMA mapping, and for the last slot (SMID num_reqs-1) write past the mapping entirely. No minimum check exists anywhere in the driver (only a printf at mps_table.c:221).