OOB heap write via unsigned underflow in slot arithmetic in _mapping_add_new_device/_mapping_add_new_pcie_device
Summary
_mapping_add_new_device at mpr_mapping.c:1637: map_idx=start_index+phy_change->slot-et_entry->start_slot. If slot<start_slot -> int subtraction yields negative -> promotes to u32 wrapping to ~0xFFFFFFFF. mt_entry=&mapping_table[map_idx] (:1639) -> OOB write. _mapping_add_new_pcie_device identical at :1894. slot from SAS/PCIe Device Page 0 (firmware u16), start_slot from Enclosure Status Change event (firmware u16). Runtime trigger via topology change event. Fix: check slot>=start_slot, check map_idx<max_devices and in enclosure range.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1283 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | object-level proof: replays _mapping_add_new_device slot arithmetic (OOB + underflow cases) | 4.3 KB | view raw |
| fix.diff | suggested-fix | adds slot<start_slot / map_idx>=max_devices guards to SAS and PCIe paths | 1.5 KB | view raw |
| build.sh | repro-script | cc -O2 -o harness harness.c | 101 B | view raw |
| run.sh | repro-script | ./harness | 60 B | view raw |
| build.log | build-log | harness build, full output | 65 B | view raw |
| run.log | run-log | harness decisive run: Case A OOB idx 310, Case B wild ptr 0xfffffffb | 758 B | view raw |
| env.txt | environment | uname + cc version | 188 B | view raw |
| README.md | readme | summary + reproduce | 2.3 KB | β raw |
| VERDICT.md | verdict | full mechanism + reachability + fix | 3.8 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-1283 β OOB heap write via unsigned underflow / missing bounds check in slot arithmetic (_mapping_add_new_device / _mapping_add_new_pcie_device)
File: sys/dev/raid/mpr/mpr_mapping.c:1637 (SAS), :1894 (PCIe)
Class: CWE-787 Out-of-bounds Write (heap) + unsigned integer underflow
Severity: High
The bug (source-confirmed)
In _mapping_add_new_device (and the PCIe twin _mapping_add_new_pcie_device),
the mapping-table index for a newly added device is computed at :1637:
map_idx = et_entry->start_index + phy_change->slot - et_entry->start_slot;
mt_entry = &sc->mapping_table[map_idx]; /* :1639, unguarded */
Types (verified): map_idx is u32 (:1579), phy_change->slot is uint16_t
(mpr_mapping.h:48), et_entry->start_slot is u16 (mprvar.h:148),
et_entry->start_index is u32 (mprvar.h:143). mapping_table is allocated
for max_devices entries (:2140).
There is no check that slot >= start_slot, and no check that
map_idx < max_devices. Both slot (SAS/PCIe Device Page 0) and start_slot
(Enclosure Status Change event) are firmware-controlled u16 values. Two failure
modes result:
- slot too large:
start_index + slot - start_slot >= max_devicesβ straight OOB heap write past the allocation. - underflow:
slot < start_slotβ(start_index + slot) - start_slotwraps to a hugeu32(e.g.0xFFFFFFFB) β wild out-of-slab pointer dereference.
Reachability / threat model
Same as DF-1282: the mpr driver is hardware-bound (LSI/Avago SAS HBA), not
present in the audit QEMU guest, so not runtime-reachable here. The path runs
on SAS/PCIe topology-change events, so a malicious peripheral / expander /
firmware supplying a crafted slot or enclosure start_slot triggers it on real
hardware. See VERDICT.md.
Reproduce (harness)
./build.sh && ./run.sh
Decisive output:
Case A: map_idx=310 >= max_devices=264 -> write at mapping_table[310] (OOB, adjacent slab) Case B: map_idx=4294967291 (0xfffffffb) -- WILD POINTER (underflow)
Fix
fix.diff adds slot < start_slot || map_idx >= sc->max_devices guards to both
the SAS and PCIe paths (continue on violation). Validated: applies cleanly and
the mpr module compiles with -Werror in-tree.
DF-1283 β VERDICT
Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest).
Mechanism (source trace)
_mapping_add_new_device (sys/dev/raid/mpr/mpr_mapping.c:1573) processes a
SAS topology-change list and inserts each newly-added device into the mapping
table.
-
Index arithmetic β
mpr_mapping.c:1637-1638:c map_idx = et_entry->start_index + phy_change->slot - et_entry->start_slot;Types:map_idxu32(:1579);phy_change->slotuint16_t(mpr_mapping.h:48);et_entry->start_slotu16(mprvar.h:148);et_entry->start_indexu32(mprvar.h:143).slotcomes from the SAS Device Page 0;start_slotfrom the Enclosure Status Change event β both firmware-controlled. -
Unguarded sink β
mpr_mapping.c:1639:c mt_entry = &sc->mapping_table[map_idx]; /* no bounds check */mapping_tablehasmax_devicesentries (:2140). -
Identical twin at
:1894-1896in_mapping_add_new_pcie_device.
There is no slot >= start_slot guard and no map_idx < max_devices
guard. Two OOB write modes:
- slot too large (Case A in the harness):
start_index + slot - start_slotexceedsmax_devicesβ write past the allocation into the adjacent slab object. - underflow (Case B):
slot < start_slotβ the unsigned result wraps to a value near0xFFFFFFFFβ wild out-of-slab pointer write (in-kernel: panic / wild write; in userspace: SIGSEGV).
Primitive characterization
- Write size: one
struct dev_mapping_tableentry (32 bytes), attacker-shaped (physical_id,dev_handle,device_info). - Target:
mapping_tableslab adjacency, or β in the underflow case β a near-arbitrary kernel address (controlled only bystart_index/slot/start_slotmagnitude).
Harness proof
harness.c replays the exact arithmetic. Output (run.log):
Case A: start_index=10 slot=300 start_slot=0 -> map_idx=310 (0x136) BUG: map_idx=310 >= max_devices=264 -> write at mapping_table[310] (adjacent-slab corruption) Case B: start_index=0 slot=0 start_slot=5 -> map_idx=4294967291 (0xfffffffb) BUG: ... -- WILD POINTER (in-kernel: out-of-slab write / panic) RESULT: OOB write into adjacent slab region DEMONSTRATED
The underflow case produces 0xFFFFFFFB β exactly the wild pointer the kernel
would dereference.
Why not a live in-kernel reproduction (valid hard blocker)
Same as DF-1282: mpr is hardware-bound (no SAS HBA in the QEMU guest), so the
topology-change path that calls _mapping_add_new_device never runs here. Live
trigger conditions: an mpr-attached HBA plus a topology event carrying a
malicious slot or enclosure start_slot (malicious peripheral / expander /
firmware / NVRAM corruption / passed-through HBA to a malicious VM). Primitive
proven at the object/harness level. Escalation to uid=0 requires the primitive
to fire in a running kernel (groom the slab so the wild/adjacent write hits a
function-pointer / ucred-bearing victim) β not demonstrable on this guest
because the driver never attaches. Honest reported impact: the corruption
primitive itself.
Fix
fix.diff adds slot < start_slot || map_idx >= sc->max_devices guards to both
the SAS (:1637) and PCIe (:1894) paths, continue-ing on violation.
Validated: patch -p1 succeeds (both hunks), and the full mpr module
builds with -Werror (mpr.ko produced). Supersedes any pre-verification
proposal by covering both the SAS and PCIe twins.
Fix-validation status
not_testable for a live before/after (PoC driver path cannot run on the
guest). Evidence the fix is correct: (1) harness before/after shows the guard
closes both the OOB and the underflow; (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). mpr slot arithmetic unsigned underflow -> wild ptr or adjacent-slab OOB. No SAS HBA.
No comments yet.