# DF-4363 — mps_config_get_man_pg10: firmware-controlled DMA heap overflow

`sys/dev/raid/mps/mps_config.c` — the ONLY getter in the file that allocates a
**fixed-size** page buffer while the DMA length comes from the firmware config
page header.

* `mps_config.c:249` `cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;`
  (PageLength is U8 → up to 255 → cm_length up to 1020 bytes)
* `mps_config.c:254` `page = kmalloc(MPS_MAN_PAGE10_SIZE, ...)` with
  `MPS_MAN_PAGE10_SIZE 0x5C` = 92 bytes (`mpsvar.h:105`)
* `mps_config.c:260` `cm->cm_data = page;` → `mps_request_polled` →
  `mps.c:2099-2101` `bus_dmamap_load(..., cm->cm_data, cm->cm_length, ...)`
  → `mps_data_cb` SGEs cover **cm_length** bytes; the IOC DMAs the whole
  Manufacturing Page 10 past the end of the 92-byte heap object.

Every sibling allocation in this file and every one in the newer
`sys/dev/raid/mpr/mpr_config.c` uses `kmalloc(cm->cm_length, ...)` — this is
the one divergent site (mp r twin: mpr_config.c:145,277,450,588,724,861,998,
1130,1262,1394,1552).

Reachable at attach/discovery on WarpDrive HBAs (`mps_pci.c:143` SSS6200 →
`MPS_FLAGS_WD_AVAILABLE`; `mps_sas.c:3320` → `mps_wd_config_pages` →
`mps_config.c:368`). Same threat model as DF-1556/DF-1557: a buggy or
malicious IOC firmware (or a device posing as one, e.g. hotplug/Thunderbolt)
corrupts kernel heap memory with fully firmware-chosen bytes, up to 928 bytes
past a 92-byte `M_MPT2` allocation.

## Reproduce (on the DragonFly guest, unprivileged)

```
cc -O2 -Wall -Wextra -o harness harness.c
./harness 24     # minimal overflow: 96-byte DMA into 92-byte kmalloc -> SIGSEGV
./harness 23     # boundary: 92 bytes exactly -> clean pass
./harness 255    # max: 1020-byte DMA -> 928 bytes past the allocation -> SIGSEGV
```

The guest has no LSI HBA (see env.txt), so — exactly like the DF-1556 and
DF-1557 precedent — the harness reproduces the kernel arithmetic and the
device-write pattern in an unprivileged userland process: the 92-byte
"kmalloc" is placed against a `PROT_NONE` guard page, the firmware DMA is
modeled as a `memcpy` of `cm_length` bytes of firmware-chosen data.
`SIGSEGV` on the guard page == the heap overflow primitive.

## Fix validation

```
cc -O2 -Wall -Wextra -DFIXED -o harness_fixed harness.c
./harness_fixed 24   # exit 0, no overflow
./harness_fixed 255  # exit 0, no overflow
./harness_fixed 4    # short-page floor guard path (fix.diff hunk 2)
```

`fix.diff` (never applied to `sys/`) sizes the allocation
`MAX(cm->cm_length, MPS_MAN_PAGE10_SIZE)` and rejects pages shorter than the
`ProductSpecificInfo[5]` deref needs (`0x1C` bytes, `mps_config.c:286,297`).

## Files

* `harness.c` — reproduction source (baseline + `-DFIXED` validation modes)
* `run.sh` — exact build/run commands
* `run.log` — FULL untrimmed guest output (build + decisive runs + fixed runs)
* `env.txt` — guest uname / compiler / mps-in-GENERIC / no-HBA proof
* `fix.diff` — git-apply-able fix vs `sys/dev/raid/mps/mps_config.c`
* `VERDICT.md`, `manifest.json`, `verdict.json`
