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

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:

  1. 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
  2. An out-of-tree kld module linking against the exported mpr_config_get_raid_volume_pg0 symbol (declared in mprvar.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:

  1. A small out-of-tree kld module that calls mpr_config_get_raid_volume_pg0() against a mock or fault-injected mpr_softc whose mpr_wait_command returns a reply with Header.PageLength set to, e.g., 0x40 (64 β†’ cm_length=256 bytes), causing the bcopy at mpr_config.c:1295 to write 256 bytes into a 44-byte stack struct β€” observable as a stack-smash panic / KASAN splat in the calling module;
  2. 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.

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.

  • DF-1282/1283 (sibling, mpr_mapping): DPM DeviceIndex OOB.
  • DF-1473/1474/1475 (sibling, mpr_sas_lsi): event handler OOB family.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1491 Β· 1 files
FileTypeDescriptionSize
fix.diff suggested-fix Missing MIN() bound in mpr_config_get_raid_volume_pg0 allows firmware-controlled 363 B view raw

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

fix.diff authored but did not apply cleanly; needs context rework

fix.diff authored but did not apply cleanly; needs context rework
↓ fix.diffcombined build rc=0

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