# DF-4363 VERDICT

**Status: reproduced (harness, guest has no mps HBA — same method as DF-1556/DF-1557).**
**Impact class: kernel heap overflow via device DMA (CWE-787). Primitive certain.**

## Root cause (line-accurate)

`mps_config_get_man_pg10()` at `sys/dev/raid/mps/mps_config.c:181-314`:

1. `:248-249` — after the PAGE_HEADER round-trip, the firmware-supplied
   `mpi_reply->Header.PageLength` (U8, in the reply copied at `:221`)
   becomes `cm->cm_length = le16toh(PageLength) * 4` → **up to 1020**.
2. `:254` — the page buffer is allocated at the **fixed** size
   `MPS_MAN_PAGE10_SIZE = 0x5C = 92` bytes (`mpsvar.h:105`), the only
   fixed-size page allocation in the file. Every other getter allocates
   `cm->cm_length` (mps_config.c:136,607,730,852,970,1089,1209,1354), and
   every getter in the successor driver does the same
   (mpr_config.c:145,277,450,588,724,861,998,1130,1262,1394,1552).
3. `:260-261` — `cm->cm_data = page; mps_request_polled(sc, cm);` with
   `MPS_CM_FLAGS_DATAIN`: `mps_map_command` (`mps.c:2099-2101`) loads
   `bus_dmamap_load(..., cm->cm_data, cm->cm_length, ...)`. busdma does not
   know the malloc size — it maps `cm_length` bytes starting at the 92-byte
   allocation, and `mps_data_cb` (`mps.c:2041ff`) builds SGEs covering the
   whole map. The IOC then DMAs the full Manufacturing Page 10 into it.

Result: **firmware-controlled heap overflow of up to 928 bytes past a 92-byte
`M_MPT2` allocation**, with fully controlled content (the page bytes), at
`kfetch/malloc` heap in the config path.

The in-function derefs are NOT the bug: `page->ProductSpecificInfo` (offset
0x04, `:286`) and `pPS_info[5]` (0x18, `:297-298`) both sit inside 0x5C
(`Mpi2ManufacturingPagePS_t`, `mpi2_cnfg.h:722-727`). The bug is purely the
DMA-length vs allocation-size mismatch — which is why this is distinct from
the knowns DF-1556 (`:1117` missing-MIN bcopy) and DF-1557 (`:462`
PhysDiskMap index): neither covers this site.

## Reachability

* Attach/discovery on WarpDrive HBAs: `mps_pci.c:143` ("LSI SSS6200" →
  `MPS_FLAGS_WD_AVAILABLE`) → `mps_sas.c:3320` `mps_wd_config_pages()` →
  `mps_config.c:368` `mps_config_get_man_pg10()`.
* Threat model identical to DF-1556/DF-1557 (both rated High, this file):
  malicious/buggy firmware or a device posing as an SSS6200 (PCIe hotplug).
  Not reachable from an unprivileged process without such hardware — hence
  harness-level reproduction, per the established DF-1556/DF-1557 precedent.

## Reproduction (guest, unprivileged user maxx)

Guard-page harness mirrors the kernel arithmetic 1:1; the device DMA is a
`memcpy` of `cm_length` firmware-chosen bytes into a 92-byte buffer placed at
the end of a page followed by `PROT_NONE`:

* PageLength=24 → cm_length=96 → **SIGSEGV, 4 bytes past the allocation**
  (minimal case) — run.log `RUN24_EXIT=1`
* PageLength=23 → cm_length=92 → **exact fit, clean pass** — `RUN23_EXIT=0`
  (proves the boundary is exactly 0x5C)
* PageLength=255 → cm_length=1020 → **SIGSEGV, 928 bytes past** —
  `RUN255_EXIT=1`

## Fix validation

`fix.diff` (authored against the read-only `sys/` tree; NOT applied there):
size the buffer `MAX(cm->cm_length, MPS_MAN_PAGE10_SIZE)` and reject
`cm->cm_length < 0x1C` before the `ProductSpecificInfo[]` derefs.
Modeled in the harness via `-DFIXED`:

* baseline: PageLength 24/255 → SIGSEGV (overflow) — reproduced
* fixed: PageLength 24/255 → exit 0, no overflow; PageLength 4 → EINVAL
  floor-guard path (protects the fixed allocation's own derefs)
* `fix_status: fixed` (harness-level; a real kernel build cannot exercise
  the driver without an HBA, and the two hunks are mechanical).

Guest left clean (no kernel changes, /tmp artifacts only); no panic.

## 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.
