# DF-1557 — PoC: unchecked PhysDiskMap OOB write in mps_wd_config_pages

- **File:** `sys/dev/raid/mps/mps_config.c:462`
- **Class:** CWE-787 Out-of-bounds Write (unchecked firmware array index)
- **Severity:** High
- **Status:** REPRODUCED (source-trace + userspace harness; latent on this guest — no LSI HBA)

## Build

```sh
./build.sh    # cc -O2 -Wall -Wextra -o harness harness.c
```

## Run

```sh
./run.sh                   # defaults: PhysDiskMap=255, value=0x41, count=1 (max OOB)
./run.sh 10 65 1           # minimal OOB: PhysDiskMap=10 (+2 bytes past softc)
./run.sh 255 65 1          # max offset: PhysDiskMap=255 (+982 bytes past softc)
./run.sh 9 65 1            # control: PhysDiskMap=9 (last valid in-bounds slot)
```

Args: `<PhysDiskMap> <PhysDiskNum> <NumPhysDisks>`

## Expected output (bug present)

```
write target: &DD_column_map[10].phys_disk_num = byte offset 42 from DD_column_map base
-> 2 bytes PAST end of DD_column_map (= past end of struct mps_softc ...)
[BUGGY] running loop: sc->DD_column_map[10].phys_disk_num = 0x41 ...
>>> SIGSEGV/11 caught: controlled-byte OOB write past DD_column_map[9] confirmed.
>>> PRIMITIVE CONFIRMED: a malicious HBA returning a RAID Volume Page 0 with
    PhysDiskMap=10 writes byte 0x41 at offset +2 past the end of struct mps_softc.
>>> This is the exact code at sys/dev/raid/mps/mps_config.c:462 (no bounds check
    on PhysDiskMap).
```

The control run (`./run.sh 9 ...`) prints `PhysDiskMap=9 <
MPS_MAX_DISKS_IN_VOL=10: in-bounds write, no OOB.` and exits cleanly,
confirming the overflow boundary is exactly at `PhysDiskMap=10`.

## How it works

The harness reproduces the exact C logic of the kernel
`mps_wd_config_pages()` PhysDisk mapping loop:

```c
/* mps_config.c:460 */  pRVPD = (pMpi2RaidVol0PhysDisk_t)&raid_vol_pg0->PhysDisk;
/* mps_config.c:461 */  for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
/* mps_config.c:462 */      sc->DD_column_map[pRVPD->PhysDiskMap].phys_disk_num =
/* mps_config.c:463 */          pRVPD->PhysDiskNum;
/* mps_config.c:464 */      pRVPD++;
/*                       }
```

The loop counter `index` is bounded by `NumPhysDisks` (≤8 by the line-400
gate), but the write index `pRVPD->PhysDiskMap` is an unchecked `U8` (0..255)
straight from the firmware reply.  `DD_column_map[10]` is the first slot
past the array, and because `DD_column_map` is the **final field** of `struct
mps_softc` (`mpsvar.h:442-443`), any `PhysDiskMap >= 10` writes past the end
of the softc allocation.  A `PROT_NONE` guard page placed immediately after
the `DD_column_map` tail makes even the minimal OOB write fault crisply.

## Why a harness (not a live trigger)?

The audit guest has no LSI SAS HBA, so the `mps(4)` driver — although
compiled into `X86_64_GENERIC` — never attaches, and the live
`mps_wd_config_pages()` code path is never executed at runtime on this
guest.  The harness reproduces the exact kernel C logic in userspace to
prove the primitive; the bug itself is confirmed by source trace at
`sys/dev/raid/mps/mps_config.c:462`.

## Fix

See `fix.diff` — add an explicit `PhysDiskMap >= MPS_MAX_DISKS_IN_VOL` bounds
check before the indexed write, routing to the same `goto out` cleanup every
other validation failure in this function uses.  Matches the finding
proposal.  Validated to apply cleanly and compile into the single-fix `#1`
kernel with `-Werror` and zero warnings; see `VERDICT.md` and `fix_run.log`.

## Files

- `harness.c` — userspace reproduction of the kernel mapping loop
- `build.sh`, `run.sh` — exact reproducible commands
- `build.log`, `run.log`, `run.2.log`, `run.3.log` — full run logs
- `fix.diff` — git-apply-able fix (matches the finding proposal)
- `fix_build.log`, `fix_run.log` — Phase 8 kernel-build validation
- `env.txt` — guest environment
- `VERDICT.md` — full narrative
- `manifest.json` — machine-readable catalog
