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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1375 Β· 12 files
FileTypeDescriptionSize
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
README.md readme summary + reproduce
↓ download 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).

VERDICT.md verdict full mechanism + reachability + fix
↓ download raw

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_slots consecutive 32-byte entries past the table.
  • Target: the mapping_table kmalloc 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_testable

compile+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.