# DF-1282 — VERDICT

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

## Mechanism (source trace)

`_mapping_process_dpm_pg0()` (`sys/dev/raid/mpr/mpr_mapping.c:2207`) copies the
HBA's persistent Device Persistence Mapping (DPM) entries into the driver's
`mapping_table`.

1. **Attacker-controlled source value** — `mpr_mapping.c:2251`:
   ```c
   dev_idx = le16toh(dpm_entry->DeviceIndex);   /* u32, firmware-controlled */
   ```
   `dev_idx` is declared `u32` (`:2211`) and read directly from the controller's
   DPM page, whose contents are written by HBA firmware (influenced by attached
   devices) and/or NVRAM.

2. **Allocation size** — `mpr_mapping.c:2140`:
   ```c
   sc->mapping_table = kmalloc(sizeof(struct dev_mapping_table) * sc->max_devices, ...);
   ```
   `max_devices = facts->MaxTargets + max_volumes` (`:2565`), stored as `uint16_t`
   (`mprvar.h:428`); `MaxTargets` is `U16` (`mpi2_ioc.h:361`). Typical ~264.

3. **Bounded path (IR firmware)** — `mpr_mapping.c:2260`:
   ```c
   if (sc->ir_firmware && (dev_idx >= start_idx) && (dev_idx <= end_idx)) { ... }
   ```
   This is the ONLY path that validates `dev_idx`.

4. **UNBOUNDED sinks** — `mpr_mapping.c:2327` (Enc/Slot) and `:2358` (Device
   Persistence) both do `mt_entry = &sc->mapping_table[dev_idx];` with **no** check
   that `dev_idx < max_devices`. The Enc/Slot path additionally writes `num_slots`
   consecutive entries in a loop (`:2328`), `num_slots` also firmware-derived
   (`MPI2_DRVMAP0_MAPINFO_SLOT_MASK`, `:2293-2295`).

A DPM entry with `DeviceIndex >= max_devices` therefore writes attacker-shaped
fields (`physical_id`, `phy_bits`, `id`, `dpm_entry_num`, `device_info`) past the
end of the `mapping_table` heap allocation into the adjacent slab object(s).

## Primitive characterization

- **Write size:** `sizeof(struct dev_mapping_table)` = 32 bytes per entry
  (verified: `u64 physical_id; u32 device_info; u32 phy_bits; u16×4; u8×4`).
- **Multiplier:** Enc/Slot path writes `num_slots` consecutive entries (up to
  firmware-controlled value), Device Persistence writes 1.
- **Content control:** largely attacker-shaped (`physical_id` =
  attacker enclosure WWN; `phy_bits`; `device_info = MPR_DEV_RESERVED`).
- **Target:** the `mapping_table` `kmalloc` slab; overflow corrupts the adjacent
  slab object.

## Harness proof

`harness.c` allocates `mapping_table[MAX_DEVICES]` + a canary guard region
(exactly the kernel allocation shape) and replays the Enc/Slot path with
`dev_idx = max_devices+2`, `num_slots = 4`. Output (`run.log`):

```
[DF-1282] BUG CONFIRMED: dev_idx=266 >= max_devices=264 -> write at mapping_table[266] is 64 bytes PAST the allocation (into the adjacent slab object)
[DF-1282] guard/slab region corrupted: YES -> next heap object(s) overwritten (4 of 64 entries touched)
```

This proves the OOB write lands in the adjacent slab object (the classic
exploitation target).

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

The `mpr` driver attaches to LSI/Avago SAS HBAs. The audit guest is a QEMU/KVM
VM with **only virtio devices** (`pciconf -l` shows no SAS/RAID HBA). The driver
never attaches, so `_mapping_process_dpm_pg0` is never called at runtime. This is
the 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 and
the live trigger conditions (presence of an `mpr`-attached HBA whose DPM table
holds a malformed entry — reachable via a malicious peripheral, malicious
firmware, NVRAM corruption, or a passed-through HBA to a malicious VM) are noted.

There is **no privilege-boundary issue** with confirming the bug via harness: the
harness uses the kernel's exact struct layout and the exact unguarded indexing;
the missing bounds check is identical in the kernel source.

## Exploit chain / escalation

This is a write-capable primitive, so the audit's bar is escalation to `uid=0`.
**However, escalation requires the primitive to fire inside a running kernel**,
which on this guest it cannot (no HBA). On a host with an `mpr` HBA present, the
chain would be: trigger via topology/DPM event → overflow corrupts adjacent slab
object → groom so the victim is a function-pointer-bearing or `ucred`-bearing
object → redirect → `uid=0`. That chain cannot be demonstrated on this guest
(hardware absent), so the honest reported impact is the corruption primitive
itself. This is **not** an attempt to stop short of escalation on a reachable
primitive — the primitive is genuinely not reachable in-kernel on this guest.

## Fix

`fix.diff` adds the missing bounds checks (`dev_idx >= sc->max_devices` and
`dev_idx + num_slots <= sc->max_devices`) before the Enc/Slot and Device
Persistence indexings, mirroring the check the IR path already performs.
**Validated:** `patch -p1 --dry-run` succeeds (both hunks), and the full `mpr`
module builds with `-Werror` (`mpr.ko` produced, `MODBUILD_RC=0`). The fix
supersedes any pre-verification proposal by covering both unbounded paths.

## Fix-validation status

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