# DF-1375 — VERDICT

**Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest).**

This is the **mps driver's twin of DF-1283** (the identical bug in the `mpr`
driver's `mpr_mapping.c`).

## Mechanism (source trace)

### Primary: slot underflow in `_mapping_add_new_device`

`_mapping_add_new_device()` (`sys/dev/raid/mps/mps_mapping.c:1114`) computes a
map-table index from firmware-supplied slot numbers at `mps_mapping.c:1165`:
```c
map_idx = et_entry->start_index + phy_change->slot -
          et_entry->start_slot;                      /* u32 arithmetic */
mt_entry = &sc->mapping_table[map_idx];              /* :1167 OOB */
```
`phy_change->slot` comes from `le16toh(sas_device_pg0.Slot)` (firmware,
`:1191`). `et_entry->start_slot` is from enclosure setup. **There is no check
that `slot >= start_slot`.** When `slot < start_slot`, the `u32` subtraction
wraps: e.g. `start_index=0, slot=0, start_slot=2` ⇒ `map_idx = 0xFFFFFFFE`, and
`&sc->mapping_table[0xFFFFFFFE]` is a **massive OOB write** into kernel
memory. Even when the result stays in range, a `slot < start_slot` writes the
attacker's `physical_id`/`device_info` into the **wrong** entry (below the
enclosure's `start_index`).

### Secondary: num_slots reservation loop trusts firmware

`_mapping_get_dev_info()` (`mps_mapping.c:881`) reservation loop at `:992-998`:
```c
mt_entry = &sc->mapping_table[map_idx];
for (index = map_idx; index < (et_entry->num_slots + map_idx); index++, mt_entry++) {
    mt_entry->device_info = MPS_DEV_RESERVED;
    ...
}
```
`et_entry->num_slots` is firmware-derived (`NumSlots` event, `:1984`-class) and
is **not** validated against `max_devices`; a large `num_slots` with `map_idx`
near the end writes past `mapping_table`.

## Primitive characterization

- **Primary write:** one `dev_mapping_table` (32 bytes) at a wrapped
  (`slot<start_slot`) or wrong-target index; content attacker-shaped
  (`physical_id`, `device_info`, `dev_handle`).
- **Secondary:** `num_slots` consecutive 32-byte entries past the table.
- **Target:** the `mapping_table` kmalloc slab / adjacent kernel heap.

## Harness proof

`harness.c` replays the `:1165` arithmetic with `slot < start_slot` and the
reservation-loop bound. Output (`run.log`):
```
[DF-1375] Case A2: start_index=0, slot=0, start_slot=2 -> map_idx = 4294967294 (0xfffffffe) -- wraps to ~0xFFFFFFFE -> &mapping_table[~0xFFFFFFFE]
[DF-1375] BUG CONFIRMED: slot < start_slot makes map_idx wrap/underflow -> mapping_table[4294967294] is a massive OOB write
[DF-1375] Case B: slot=1 -> map_idx=7 (expected >= start_index=10); writes attacker physical_id/device_info into WRONG entry
[DF-1375] wrote attacker data into mapping_table[7] (below the enclosure's start_index=10) -> confirmed wrong-target corruption
[DF-1375] reservation loop: base=250 + num_slots(fw)=600 = 850 > max_devices=264 -> OOB write past mapping_table
```

## Why not a live in-kernel reproduction (the valid hard blocker)

The `mps` driver attaches to LSI/Avago SAS HBAs. The audit guest is a QEMU/KVM
VM with **only virtio devices** (no SAS/RAID HBA). The driver never attaches, so
`_mapping_add_new_device`/`_mapping_get_dev_info` are never called at runtime.
Phase-6 valid hard blocker #3: the vulnerable code path is unreachable at
runtime on this guest; the primitive is proven at the object/harness level. Live
triggers: a malicious/buggy HBA, malicious firmware, NVRAM corruption, or a
passed-through HBA to a malicious VM.

## Exploit chain / escalation

Write-capable primitive, but it fires only inside a running kernel with an
mps-attached controller, which is absent on this guest. The chain cannot be
demonstrated in-kernel; the honest reported impact is the corruption primitive
itself.

## Fix

`fix.diff` (1) rejects `phy_change->slot < et_entry->start_slot` and validates
`map_idx < sc->max_devices` after the `:1165` computation, and (2) bounds-checks
`map_idx + et_entry->num_slots <= sc->max_devices` before the `:992` reservation
loop. **Validated:** `patch -p1 --dry-run` succeeds (both hunks), and a clean
`mps.ko` build succeeds (`rc=0` — `fix_build.log`; mps.ko 194048 bytes,
confirming the patched TU compiled in). Matches the finding's proposal
(`validate slot >= start_slot, map_idx < max_devices, num_slots fits`).

## Fix-validation status

`not_testable` for a *live* before/after (the driver path cannot run on the
guest). Evidence the fix is correct: (1) harness shows the guards prevent the
underflow/OOB; (2) the fix compiles cleanly in-tree under `-Werror`.
