Unvalidated slot arithmetic in topology-event handling causes heap OOB write (underflow + num_slots overflow)
Summary
_mapping_add_new_device at mps_mapping.c:1165: map_idx=start_index+slot-start_slot (u32). slot<start_slot -> underflow to ~0xFFFFFFFF. mapping_table[map_idx] OOB write. Also _mapping_get_dev_info :992-998: reservation loop trusts num_slots from firmware NumSlots (u16) without checking map_idx+num_slots<=max_devices. Twin of mpr_mapping.c DF-1283. Malicious HBA/emulator. Fix: validate slot>=start_slot, map_idx<max_devices, num_slots fits.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1375 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | object-level proof: replays _mapping_add_new_device slot underflow (slot<start_slot -> map_idx wrap) and num_slots loop | 6.0 KB | view raw |
| fix.diff | suggested-fix | validate slot>=start_slot, map_idx<max_devices, and map_idx+num_slots<=max_devices | 1.4 KB | view raw |
| build.sh | repro-script | cc -O2 -o harness harness.c | 125 B | view raw |
| run.sh | repro-script | ./harness | 60 B | view raw |
| build.log | build-log | harness build, full output | 95 B | view raw |
| run.log | run-log | harness decisive run: map_idx wrap + wrong-target + num_slots OOB | 946 B | view raw |
| fix_build.log | fix-build-log | clean mps.ko module build with fix applied, rc=0 (mps.ko 194048B) | 16.0 KB | view raw |
| env.txt | environment | uname + cc version + dev-node check | 520 B | view raw |
| README.md | readme | summary + reproduce | 1.0 KB | β raw |
| VERDICT.md | verdict | full mechanism + reachability + fix | 4.5 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1375 β _mapping_add_new_device slot underflow OOB write (mps)
Summary
_mapping_add_new_device (sys/dev/raid/mps/mps_mapping.c:1165) computes
map_idx = start_index + slot - start_slot with no check that
slot >= start_slot; a firmware slot below start_slot wraps the u32 to
~0xFFFFFFFF β mapping_table[~0xFFFFFFFF] massive OOB write. The reservation
loop at :992-998 also trusts firmware num_slots without a bounds check. Twin
of DF-1283 (mpr). No SAS HBA on the audit guest.
Reproduce
./build.sh # cc -O2 -o harness harness.c ./run.sh # ./harness
Expected: map_idx = 4294967294 (0xfffffffe) ... massive OOB write,
wrote attacker data into mapping_table[7] ... wrong-target corruption,
reservation loop ... 850 > max_devices=264 -> OOB write. Object-level proof β
mps cannot attach on the QEMU guest.
Fix
fix.diff validates slot >= start_slot, map_idx < max_devices, and
map_idx + num_slots <= max_devices. Validated to apply + compile (mps.ko,
clean build rc=0).
DF-1375 β VERDICT
Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest).
This is the mps driver's twin of DF-1283 (the identical bug in the mpr
driver's mpr_mapping.c).
Mechanism (source trace)
Primary: slot underflow in _mapping_add_new_device
_mapping_add_new_device() (sys/dev/raid/mps/mps_mapping.c:1114) computes a
map-table index from firmware-supplied slot numbers at mps_mapping.c:1165:
map_idx = et_entry->start_index + phy_change->slot -
et_entry->start_slot; /* u32 arithmetic */
mt_entry = &sc->mapping_table[map_idx]; /* :1167 OOB */
phy_change->slot comes from le16toh(sas_device_pg0.Slot) (firmware,
:1191). et_entry->start_slot is from enclosure setup. There is no check
that slot >= start_slot. When slot < start_slot, the u32 subtraction
wraps: e.g. start_index=0, slot=0, start_slot=2 β map_idx = 0xFFFFFFFE, and
&sc->mapping_table[0xFFFFFFFE] is a massive OOB write into kernel
memory. Even when the result stays in range, a slot < start_slot writes the
attacker's physical_id/device_info into the wrong entry (below the
enclosure's start_index).
Secondary: num_slots reservation loop trusts firmware
_mapping_get_dev_info() (mps_mapping.c:881) reservation loop at :992-998:
mt_entry = &sc->mapping_table[map_idx];
for (index = map_idx; index < (et_entry->num_slots + map_idx); index++, mt_entry++) {
mt_entry->device_info = MPS_DEV_RESERVED;
...
}
et_entry->num_slots is firmware-derived (NumSlots event, :1984-class) and
is not validated against max_devices; a large num_slots with map_idx
near the end writes past mapping_table.
Primitive characterization
- Primary write: one
dev_mapping_table(32 bytes) at a wrapped (slot<start_slot) or wrong-target index; content attacker-shaped (physical_id,device_info,dev_handle). - Secondary:
num_slotsconsecutive 32-byte entries past the table. - Target: the
mapping_tablekmalloc slab / adjacent kernel heap.
Harness proof
harness.c replays the :1165 arithmetic with slot < start_slot and the
reservation-loop bound. Output (run.log):
[DF-1375] Case A2: start_index=0, slot=0, start_slot=2 -> map_idx = 4294967294 (0xfffffffe) -- wraps to ~0xFFFFFFFE -> &mapping_table[~0xFFFFFFFE] [DF-1375] BUG CONFIRMED: slot < start_slot makes map_idx wrap/underflow -> mapping_table[4294967294] is a massive OOB write [DF-1375] Case B: slot=1 -> map_idx=7 (expected >= start_index=10); writes attacker physical_id/device_info into WRONG entry [DF-1375] wrote attacker data into mapping_table[7] (below the enclosure's start_index=10) -> confirmed wrong-target corruption [DF-1375] reservation loop: base=250 + num_slots(fw)=600 = 850 > max_devices=264 -> OOB write past mapping_table
Why not a live in-kernel reproduction (the valid hard blocker)
The mps driver attaches to LSI/Avago SAS HBAs. The audit guest is a QEMU/KVM
VM with only virtio devices (no SAS/RAID HBA). The driver never attaches, so
_mapping_add_new_device/_mapping_get_dev_info are never called at runtime.
Phase-6 valid hard blocker #3: the vulnerable code path is unreachable at
runtime on this guest; the primitive is proven at the object/harness level. Live
triggers: a malicious/buggy HBA, malicious firmware, NVRAM corruption, or a
passed-through HBA to a malicious VM.
Exploit chain / escalation
Write-capable primitive, but it fires only inside a running kernel with an mps-attached controller, which is absent on this guest. The chain cannot be demonstrated in-kernel; the honest reported impact is the corruption primitive itself.
Fix
fix.diff (1) rejects phy_change->slot < et_entry->start_slot and validates
map_idx < sc->max_devices after the :1165 computation, and (2) bounds-checks
map_idx + et_entry->num_slots <= sc->max_devices before the :992 reservation
loop. Validated: patch -p1 --dry-run succeeds (both hunks), and a clean
mps.ko build succeeds (rc=0 β fix_build.log; mps.ko 194048 bytes,
confirming the patched TU compiled in). Matches the finding's proposal
(validate slot >= start_slot, map_idx < max_devices, num_slots fits).
Fix-validation status
not_testable for a live before/after (the driver path cannot run on the
guest). Evidence the fix is correct: (1) harness shows the guards prevent the
underflow/OOB; (2) the fix compiles cleanly in-tree under -Werror.
Fix verification
not_testablecompile+harness validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (harness). mps_mapping slot underflow -> map_idx wraps to ~0xFFFFFFFE -> massive OOB. mps in GENERIC, no SAS HBA. Twin of DF-1283.
No comments yet.