# DF-4089 — Event element-count walks never validated against the event frame length (SAS topo `NumEntries`, IR `NumElements`)

**File:** `sys/dev/raid/mps/mps_mapping.c` (topo walk `:2043-2063`; IR walks
`:2155-2187` and `:2185-2260`; `_mapping_check_update_ir_mt_idx:2105-2121`)
**Class:** CWE-125 out-of-bounds read of the kmalloc'd event frame (mps twin
of mpr sibling finding DF-3919)
**Severity:** Low — pass 2 (GLM 5.3), bucket `kernleak`
**Confidence:** likely

## The bug

`mpssas_evt_handler` (`mps_sas_lsi.c:141-145`) copies the event payload into
a kernel buffer sized **only** by the reply's `EventDataLength*4` and the
deferred task then hands the raw pointer to the mapping engine. The mapping
engine walks the variable arrays by their **independent** count fields with
no cross-check:

```c
/* mps_mapping.c:2043-2063 */
num_entries = event_data->NumEntries;               /* U8, frame offset 0x08 */
...
for (i = 0; i < num_entries; i++, event_phy_change++, phy_change++)
        phy_change->dev_handle = le16toh(event_phy_change->AttachedDevHandle);
        /* PHY[i] starts at frame offset 0x0C, 4 bytes each */
```

```c
/* mps_mapping.c:2161 / 2187 — same pattern */
for (i = 0; i < event_data->NumElements; i++, element++)   /* U8 count */
        ... element->ElementFlags / ReasonCode / VolDevHandle ...          /* 8B each */
```

`_mapping_check_update_ir_mt_idx:2105-2107` re-walks
`&event_data->ConfigElement[i]` the same way. Nothing relates
`NumEntries`/`NumElements` to `EventDataLength`; a (buggy or malicious)
firmware frame whose count exceeds the copied length makes these loops read
up to ~1 KB (topo: 255×4 B) / ~2 KB (IR: 255×8 B) past the `kmalloc`'d
buffer — uninitialised/adjacent kernel heap contents, M_ZERO-allocated, so
typically neighbouring allocation data.

The garbage read is then *used*: `dev_handle`/`reason` values feed
`_mapping_update_missing_count`, `_mapping_get_dev_info`,
`_mapping_add_new_device` (handles → removal table, config-page fetches,
slot math) and `mpssas_fw_work`'s own walk (`mps_sas_lsi.c:205-237`, same
unvalidated count) — mapping corruption / wrong-device addressing on
firmware-controlled garbage. No direct copyout of the over-read bytes to
userspace was traced (kernleak bucket is conservative: disclosure would need
a second sink).

Related latent amplifier in the same producer (noted, out of this file):
`sz = le16toh(event->EventDataLength) * 4` is itself a `u16` computation
(`mps_sas_lsi.c:126,141`) that wraps to 0 at `EventDataLength == 0x4000`.

## Fix

`fix.diff` (against `mps_sas_lsi.c` — the producer is the right place since
the consumers never see the frame length): widen `sz` to `u32`, floor the
allocation at the maximum walk extent implied by the event's own count
fields (topo: `offsetof(PHY) + NumEntries*4`; IR:
`offsetof(ConfigElement) + NumElements*8`), and bcopy only the genuine
`EventDataLength*4` bytes. Consumers then always read within the allocation
(zero-fill makes the surplus benign: `dev_handle == 0` entries are skipped).
Compile-validated with the batch (`-Werror`, `mps.ko` linked —
`fix_build.log` in DF-4085's pack).
