dpm_pg0_sz truncated to u16 in mps_mapping_initialize: DPM page silently under-read (benign direction proven)
Summary
uint16_t dpm_pg0_sz assigned 8 + max_dpm_entries*20; for MaxPersistentEntries >= 3277 the u16 wraps and the DPM read uses the truncated value. The allocation side is u32 and the config copy bounds with MIN() - under-read only: entries beyond ~3275 silently load as zero/unused. Functional impact (DPM persistence dropped), no memory-safety impact. u32-widen fix across 3 files.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4090 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| env.txt | β | 1.4 KB | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| manifest.json | β | 555 B | view raw | |
| verdict.json | β | 2.1 KB | view raw |
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
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 + 20N 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).
DF-4090 VERDICT
Status: untested β hardware-gated (no mps HBA on the audit guest,
env.txt); Low-severity truncation with proven-benign direction, PoC not
meaningful. Confidence: certain (arithmetic + copy bound verified).
- Truncation:
mps_mapping.c:1721declaresuint16_t dpm_pg0_sz;:1774-1776assigns8 + max_dpm_entries * sizeof(entry)wheresizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY) == 20(mpi2_cnfg.h:2721-2730: 8+2+2+4+4). ForMaxPersistentEntries β₯ 3277the value exceeds 65535 and wraps; the wrapped (smaller) size is passed tomps_config_get_dpm_pg0at:1780-1781. - Allocation side is correct:
mps_mapping_allocate_memory:1391,1418-1421computes inuint32_t; buffer is always β₯ the truncated read size. - Copy safety:
mps_config.c:541memset(config_page, 0, sz)(sz β€ alloc) andmps_config.c:635bcopy(page, config_page, MIN(cm->cm_length, sz))β bounded by sz. Under-read only. (Had the copy been bounded bycm->cm_lengthβ firmwareExtPageLengthβ this would instead be a heap overflow; theMIN()is the load-bearing guard and is present.) - Effect: DPM entries beyond ~index 3275 remain zero β treated as unused at
_mapping_process_dpm_pg0:1486-1488β persistent mapping silently lost, re-derived from topology events. Functional impact only. - Fix:
fix.diffwidens the local and theszparameter (definitionmps_config.c:529-531, declarationmpsvar.h:718-719) tou32. Compile-validated: applied to guest/usr/srcwith the batch's mps fixes;makeinsys/dev/raid/mpswith-Werrorlinkedmps.ko(BUILD_EXIT=0;fix_build.login the DF-4085 pack)./usr/srcrestored 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_testableApplied 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-4090/fix.diff, findings/poc/DF-4085/fix_build.log
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (truncation + MIN() bound analysis), fix.diff (3 files), fix_build.log in DF-4085 pack
PoC changes
n/a (untested)
Verified recommended fix
Widen dpm_pg0_sz and mps_config_get_dpm_pg0's sz parameter to u32.
Verdict
Low - integer truncation with proven-benign direction, PoC not meaningful. mps_mapping_initialize declares uint16_t dpm_pg0_sz (:1721) and assigns 8 + max_dpm_entries*20 (:1774-1776); for MaxPersistentEntries >= 3277 the u16 wraps and the DPM read at :1780 uses the truncated (smaller) size. mps_config_get_dpm_pg0 memsets and bcopies with MIN(cm->cm_length, sz) (mps_config.c:541,635) so the copy is always bounded by sz into the full-size M_ZERO allocation - under-read only (persistent entries beyond ~3275 read as zero/unused). The sz parameter plumbing is u16 in mps_config.c:530 and mpsvar.h:719; fix widens all three to u32 and compile-validates with the batch.
No comments yet.