# DF-4090 — `dpm_pg0_sz` truncated to `u16` in `mps_mapping_initialize` — DPM page silently under-read (integer truncation, benign direction proven)

**File:** `sys/dev/raid/mps/mps_mapping.c:1721,1774-1789` (+ `u16 sz`
parameter plumbing in `mps_config.c:529-531` and `mpsvar.h:718-719`)
**Class:** CWE-197 integer truncation (checklist item (d): persistent
mapping config page copy sizes vs kmalloc)
**Severity:** Low — pass 2 (GLM 5.3), bucket `base:dev`
**Confidence:** certain

## The bug

```c
uint16_t volume_mapping_flags, dpm_pg0_sz;          /* :1721 — u16! */
...
dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) +
    (sc->max_dpm_entries *
     sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY));   /* :1774-1776: 8 + 20*N */
retry_read_dpm:
        if (mps_config_get_dpm_pg0(sc, &mpi_reply, sc->dpm_pg0,
            dpm_pg0_sz)) {                          /* :1780-1781 */
```

The allocation in `mps_mapping_allocate_memory:1418-1421` computes the size
in `uint32_t` (8 + 20*N bytes, N = `ioc_pg8.MaxPersistentEntries`, U16), but
the *read* size passed to `mps_config_get_dpm_pg0()` is held in a `u16` and
truncates for N ≥ 3277 (8 + 20·3277 = 65548 > 65535). The function's `sz`
parameter is itself `u16` (`mps_config.c:530`, decl `mpsvar.h:719`).

## Direction analysis (why Low, not memory-unsafe)

`mps_config_get_dpm_pg0` does `memset(config_page, 0, sz)` then finishes with
`bcopy(page, config_page, MIN(cm->cm_length, sz))` (`mps_config.c:635`) — the
copy is always bounded by the truncated (smaller) `sz` into the full-size
M_ZERO allocation, so this can only **under-read**, never overflow:
persistent entries beyond index ~3275 silently read as zero (unused) instead
of their NVRAM contents. Impact is functional (DPM persistence silently
dropped for large tables; mapping rebuilt from events), requiring a
malicious/odd firmware reporting `MaxPersistentEntries ≥ 3277` — real HBAs
report small values. Worth fixing while the file's bounds are being
hardened; not exploitable memory corruption.

## Fix

`fix.diff` (3 files): `uint32_t dpm_pg0_sz` in `mps_mapping_initialize`;
widen `mps_config_get_dpm_pg0`'s `sz` to `u32` in definition
(`mps_config.c`) and declaration (`mpsvar.h`). Compile-validated with the
batch (`-Werror`, `mps.ko` linked — `fix_build.log` in DF-4085's pack).
