# DF-4087 — `_mapping_get_dpm_idx_from_id` reads `PhysicalIdentifier` once (entry 0) and never refreshes it — DPM lookups only ever match entry 0

**File:** `sys/dev/raid/mps/mps_mapping.c:505-525`
**Class:** CWE-697 wrong-variable / stale-read logic defect (mps twin of the
mpr sibling finding DF-3916, line-for-line identical defect)
**Severity:** Low — pass 2 (GLM 5.3), bucket `base:dev`
**Confidence:** certain

## The bug

```c
dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + hdr);
PhysicalIdentifier = dpm_entry->PhysicalIdentifier.High;      /* entry 0 only */
PhysicalIdentifier = (PhysicalIdentifier << 32) |
    dpm_entry->PhysicalIdentifier.Low;
for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++,
    dpm_entry++)                                                  /* ptr walks */
        if ((id == PhysicalIdentifier) &&                       /* value doesn't */
            (!phy_bits || !dpm_entry->PhysicalBitsMapping ||
            (phy_bits & dpm_entry->PhysicalBitsMapping)))      /* these do */
                return entry_num;
```

The 64-bit `PhysicalIdentifier` is loaded **once, from entry 0**, outside the
loop; the loop advances the pointer but never re-reads the identifier, so the
equality test compares *every* candidate entry against entry 0's ID. The
phy-bits term (which does dereference the walking pointer) is evaluated only
after the (bogus) equality already succeeded.

## Impact

`_mapping_get_dpm_idx_from_id()` is the DPM lookup used by
`_mapping_add_new_device:1179` (enclosure mapping) and
`_mapping_update_ir_missing_cnt:591` (volumes). With a populated DPM page
(>1 non-zero entry), every lookup for an ID ≠ entry-0's ID returns
`MPS_DPM_BAD_IDX`:

- the caller then takes the `_mapping_get_free_dpm_idx()` path and allocates
  a *duplicate* DPM entry for an enclosure/volume that already has one,
- enclosure/volume persistence entries get double-booked, `dpm_entry_used[]`
  fills prematurely, and `et_entry->dpm_entry_num`/`mt_entry->dpm_entry_num`
  can point at different entries for the same device — persistent-mapping
  divergence across reboots (NVRAM state corruption, functional, no direct
  memory unsafety: all indices stay within `max_dpm_entries`).

An ID that *happens to equal* entry 0's identifier is matched at every index
whose phy-bits term passes, returning a wrong (but in-bounds) entry — again
mapping divergence, not OOB.

## Threat model / reachability

Requires DPM enabled (`MaxPersistentEntries != 0`) and more than one
persistent entry — ordinary legitimate configurations. Malicious firmware
amplifies it (deliberate aliasing). Hardware-gated for in-kernel
reproduction (no mps HBA on the audit guest, `env.txt`); the defect is
source-certain and identical to the mpr sibling already filed as DF-3916.

## Fix

`fix.diff` — move the two `PhysicalIdentifier` loads inside the loop body
(braces added since the body is now two statements).
Compile-validated with the batch's other mps fixes (`-Werror`, `mps.ko`
linked — `fix_build.log` in DF-4085/DF-4086's packs; identical build).
