# DF-1328 — mpr_user_event_report kernel-heap info leak

## Verdict
**INCONCLUSIVE (real bug, needs hardware absent from guest).**  Source
trace confirms the vulnerability; it cannot be executed on this QEMU
guest because `/dev/mpr0` does not exist (no LSI SAS3+ HBA), so the PoC
fails at `open()` with `ENOENT`.  Fix validated to apply + compile in a
full `nativekernel` build.

## Mechanism

```
sys/dev/raid/mpr/mpr_user.c:2074  mpr_lock(sc);
sys/dev/raid/mpr/mpr_user.c:2075  size = data->Size;                         // uint32_t, user-controlled
sys/dev/raid/mpr/mpr_user.c:2076  if ((size >= sizeof(sc->recorded_events)) && (status == 0)) {
sys/dev/raid/mpr/mpr_user.c:2077      mpr_unlock(sc);
sys/dev/raid/mpr/mpr_user.c:2078      if (copyout((void *)sc->recorded_events,
sys/dev/raid/mpr/mpr_user.c:2079          PTRIN(data->PtrEvents), size) != 0)   // *** LEAK: len = user size ***
sys/dev/raid/mpr/mpr_user.c:2080          status = EFAULT;
sys/dev/raid/mpr/mpr_user.c:2081      mpr_lock(sc);
```

`sizeof(sc->recorded_events)` = `MPR_EVENT_QUEUE_SIZE` (200) ×
`sizeof(mpr_event_entry_t)` (= `4+4+4*MPR_MAX_EVENT_DATA_LENGTH` =
`4+4+4*48` = 200) = **40000 bytes**
(`sys/dev/raid/mpr/mpr_ioctl.h:206`, `:207`, `:224-229`;
 `sys/dev/raid/mpr/mprvar.h:445`).

The gate at 2076 is a **lower** bound, not an upper bound: it only
requires `size >= 40000`.  The `copyout` length is the **user-supplied
`size`**, which can be up to `0xFFFFFFFF` (4 GiB).  Setting `size` to,
say, 256 KiB reads 256 KiB out of `sc->recorded_events` — 40000 bytes of
the array plus ~220 KiB of whatever follows it inside `struct mpr_softc`
(DMA bus addresses, kernel pointers, locks, command rings) and into
adjacent kernel heap.  Deterministic kernel-heap information leak.

## Reachability on this guest

Identical to DF-1327: `mpr` is compiled into GENERIC but no SAS HBA is
present → no `/dev/mpr0` → PoC fails at `open()`:
```
poc: open /dev/mpr0: No such file or directory   (RUN_EXIT=1)
```
Privilege model is the same (device `UID_ROOT/GID_OPERATOR 0640`,
`mpr_open` returns 0, `mpr_ioctl` does no `priv_check`): reachable by
root or operator-group users on an mpr-equipped host.

Phase-4(d) again: real code path, unreachable on this guest due to
absent hardware.

## Exploit chain

None — read-only OOB info leak, no corruption primitive.  Impact ceiling:
deterministic disclosure of the `mpr_softc` tail (DMA addresses, kernel
pointers) and adjacent heap, repeated to taste.  KASLR-bypass / heap
layout reconnaissance.

## PoC changes

Folder was empty.  Authored `poc.c` (opens `/dev/mpr0`, issues
`MPTIOCTL_EVENT_REPORT` with `Size = 256 KiB`, dumps bytes 40000..40256 of
the softc tail), `build.sh`, `run.sh`.

## Fix

`fix.diff` makes the copyout length `sizeof(sc->recorded_events)` (the
fixed array size) instead of the user-controlled `size`.  The `size >=`
gate remains the lower-bound "did the user give us a big enough buffer?"
check; the actual bytes copied are now exactly the array.  Applies
cleanly and was built in the full nativekernel run (`fix_build.log`).

## Fix validation

`fix_status: not_testable` — PoC cannot run (no `/dev/mpr0`).  Fix
validated by apply + compile + code-path inspection (copyout length is now
the constant array size, independent of `data->Size`).
