Missing MIN() bound in mpr_config_get_raid_volume_pg0 allows firmware-controlled heap-buffer overrun into caller struct
- File:
sys/dev/raid/mpr/mpr_config.c - Lines: 1295
- Severity: Info
- CVSS:
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U:C:H/I:H/A:H - CWE: CWE-787 Out-of-bounds Write
- Confidence: speculative
Summary
mpr_config_get_raid_volume_pg0 copies the firmware-supplied config page into
the caller's Mpi2RaidVolPage0_t *config_page using the raw firmware-controlled
cm->cm_length as the byte count, without clamping to
sizeof(Mpi2RaidVolPage0_t).
Every sibling getter in this file
(mpr_config_get_ioc_pg8:180, iounit_pg8:312, dpm_pg0:484,
sas_device_pg0:759, pcie_device_pg0:896, pcie_device_pg2:1033,
bios_pg3:1165, raid_volume_pg1:1429, raid_pd_pg0:1585) wraps the length in
MIN(cm->cm_length, sizeof(Type_t)); this one alone does not.
A malicious or buggy HBA firmware reply with Header.PageLength > 11 (each unit
is 4 bytes; Mpi2RaidVolPage0_t is 0x2C = 44 bytes per mpi2_cnfg.h:1807-1828)
would cause up to 1020 bytes to be copied into a 44-byte destination, overflowing
the caller's stack/heap struct.
The function is currently dead code in-tree (zero callers across sys/,
verified by grep), so this is filed as a defense-in-depth / latent-bug finding.
Root cause
mpr_config.c:1295 reads bcopy(page, config_page, cm->cm_length);.
cm->cm_length is derived directly from the firmware reply:
mpr_config.c:1257
cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
where mpi_reply->Header.PageLength is the U8 (mpi2_cnfg.h:276) value the IOC
returned for RAID Volume Page 0.
The destination pointer config_page points to a caller-allocated
Mpi2RaidVolPage0_t (44 bytes, sizeof verified against struct definition at
mpi2_cnfg.h:1807-1828).
All nine sibling config getters in the same file clamp the bcopy with MIN();
this one was missed.
The internal page buffer is itself safely sized (kmalloc(cm_length) at line
1262), so the overrun is purely on the destination config_page, not the source.
Threat
Attacker position requires either:
- A malicious or compromised LSI SAS3/Avago IOC firmware that returns an oversized RAID Volume Page 0 header (typical via PCI passthrough of a hostile device into a VM, supply-chain HBA tampering, or a firmware bug), or
- An out-of-tree
kldmodule linking against the exportedmpr_config_get_raid_volume_pg0symbol (declared inmprvar.h:790) that drives the config-request path.
In the current DragonFlyBSD tree there are zero callers (grep over sys/
returns only the declaration and the definition), so there is no reachable
in-kernel trigger; this is the reason the finding is rated Info/speculative
rather than High.
If a future change wires this getter into the RAID-mapping or user-ioctl path (as its siblings already are), the same hostile-firmware precondition would yield a kernel stack/heap overwrite of up to ~976 bytes past the destination, sufficient for arbitrary code execution in ring 0.
Exploit / PoC
No in-tree syscall path reaches this code, so a standard userland PoC cannot trigger the bug.
Reproduction of the latent defect requires either:
- A small out-of-tree
kldmodule that callsmpr_config_get_raid_volume_pg0()against a mock or fault-injectedmpr_softcwhosempr_wait_commandreturns a reply withHeader.PageLengthset to, e.g.,0x40(64 βcm_length=256bytes), causing thebcopyatmpr_config.c:1295to write 256 bytes into a 44-byte stack struct β observable as a stack-smash panic / KASAN splat in the calling module; - PCI passthrough of a hostile emulated SAS3 HBA into a DragonFly guest where the emulated IOC returns an oversized RAID Volume Page 0 header.
For the orchestrator: a kld-based PoC skeleton would be placed at
findings/poc/DF-1491/ as trigger_kld.c + Makefile, but verification in the
default QEMU/KVM guest (no SAS3 HBA) will classify as missing-setup/unreachable;
the bug is provable by static analysis alone (line 1295 vs the nine
MIN()-guarded siblings).
Recommended verification path is code review + the fix diff below; runtime PoC is gated on hardware/FIU availability.
Recommended fix
Clamp the destination copy by sizeof(Mpi2RaidVolPage0_t), matching every
sibling getter in this file.
--- a/sys/dev/raid/mpr/mpr_config.c
+++ b/sys/dev/raid/mpr/mpr_config.c
@@ -1292,7 +1292,8 @@ mpr_config_get_raid_volume_pg0(struct mpr_softc *sc, Mpi2ConfigReply_t
error = ENXIO;
goto out;
}
- bcopy(page, config_page, cm->cm_length);
+ bcopy(page, config_page, MIN(cm->cm_length,
+ sizeof(Mpi2RaidVolPage0_t)));
out:
kfree(page, M_MPR);
if (cm)
MIN is already used in the surrounding siblings and is the kernel's standard
min macro (sys/systm.h); no new include required.
Optional hardening: also reject the page read if cm->cm_length < sizeof(Mpi2RaidVolPage_t)
so the caller cannot consume a truncated/uninitialized struct tail.
Related findings
- DF-1282/1283 (sibling, mpr_mapping): DPM DeviceIndex OOB.
- DF-1473/1474/1475 (sibling, mpr_sas_lsi): event handler OOB family.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1491 Β· 1 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Missing MIN() bound in mpr_config_get_raid_volume_pg0 allows firmware-controlled | 363 B | view raw |
Fix verification
not_testablefix.diff authored but did not apply cleanly; needs context rework
fix.diff authored but did not apply cleanly; needs context rework
Confirmed kernel references
β
Detail
Exploit chain
none (Info severity)
Evidence (decisive lines)
Source-confirmed at sys/dev/raid/mpr/mpr_config.c:1295: missing MIN() bound allows firmware-controlled heap-buffer overrun
Verified recommended fix
Source-confirmed at sys/dev/raid/mpr/mpr_config.c:1295: missing MIN() bound allows firmware-controlled heap-buffer overrun
Verdict
Source-confirmed at sys/dev/raid/mpr/mpr_config.c:1295: missing MIN() bound allows firmware-controlled heap-buffer overrun
No comments yet.