β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4087

_mapping_get_dpm_idx_from_id reads PhysicalIdentifier once from entry 0 and compares every DPM entry against it (mps twin of DF-3916)

Summary

The 64-bit PhysicalIdentifier is loaded from DPM entry 0 outside the loop and never re-read; the equality test compares every candidate against entry 0's ID. With >1 populated DPM entry, lookups return BAD_IDX -> duplicate entries, double-booked dpm_entry_used and divergent dpm_entry_num -> persistent-mapping corruption across reboots. All indices stay bounded (no OOB). Line-for-line identical to mpr sibling DF-3916. Re-read-per-iteration fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4087 Β· 6 files
FileTypeDescriptionSize
README.md β€” 3.0 KB ↓ raw
VERDICT.md β€” 2.0 KB ↓ raw
env.txt β€” 1.4 KB view raw
fix.diff β€” 825 B view raw
manifest.json β€” 521 B view raw
verdict.json β€” 1.9 KB view raw

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

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).

VERDICT.md
↓ download raw

DF-4087 VERDICT

Status: untested β€” hardware-gated (no mps HBA on the audit guest, env.txt); Low-severity logic class, PoC skipped per batch precedent (mpr twins DF-3916..3919). Confidence: certain (source).

  • Defect: sys/dev/raid/mps/mps_mapping.c:512-522 β€” PhysicalIdentifier loaded from DPM entry 0 at :514-516, never re-read inside the loop at :517-522; every iteration compares the sought ID against entry 0's value while dpm_entry walks past it. Byte-for-byte the same defect as mpr sibling DF-3916 (mpr_mapping.c:539-566), which was filed and source-proven in the mpr pass.
  • Consequence: DPM lookups fail (or alias to entry 0) whenever the table has more than one entry β†’ duplicate DPM allocation via _mapping_get_free_dpm_idx (mps_mapping.c:608, :1183), double-booked dpm_entry_used[] slots, enclosure/volume dpm_entry_num divergence (:1177-1226) β†’ persistent mapping state corruption across reboots. All indices remain < max_dpm_entries; no OOB β€” impact is correctness/persistence, hence Low.
  • Consumers of the lookup: _mapping_add_new_device :1179, _mapping_update_ir_missing_cnt :591 β€” both only take the lookup result as an entry number or BAD_IDX; bounded arrays (dpm_entry_used, dpm_flush_entry, dpm_pg0 walks) stay in bounds.
  • Fix: fix.diff re-reads the identifier per iteration (loop body braced). Compile-validated: applied to the guest /usr/src with the rest of the batch's mps fixes; make in sys/dev/raid/mps with -Werror linked mps.ko (BUILD_EXIT=0; fix_build.log in the DF-4085 pack). /usr/src restored pristine; guest reset with clean-source snapshot.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Applied to the guest /usr/src together with this batch's other mps fixes (DF-4085..DF-4090); make in sys/dev/raid/mps compiled all sources with -Werror and linked mps.ko (BUILD_EXIT=0). Runtime fix validation not testable: the path requires an mps(4) HBA delivering firmware data, absent on this guest.

findings/poc/DF-4087/fix.diff, findings/poc/DF-4085/fix_build.log
↓ fix.diffper-fix-DF-4087

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (line-by-line), fix.diff, fix_build.log in DF-4085 pack (same -Werror module build)

PoC changes

n/a (untested)

Verified recommended fix

Re-read PhysicalIdentifier from dpm_entry inside the per-entry loop.

Verdict

Low - PoC skipped per contract (hardware-gated logic class, mpr twin DF-3916 precedent). Source-certain: _mapping_get_dpm_idx_from_id (mps_mapping.c:512-522) loads the 64-bit PhysicalIdentifier once from DPM entry 0 and never re-reads it inside the loop, so every candidate entry is compared against entry 0's identifier; DPM lookups only ever match entry 0, all other IDs return BAD_IDX and get duplicate entries via _mapping_get_free_dpm_idx -> persistent-mapping double-booking/divergence. All indices stay < max_dpm_entries; correctness impact. Fix authored; module compile-validated with the batch.