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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1283 Β· 11 files
FileTypeDescriptionSize
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
README.md readme summary + reproduce
↓ download 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_slot wraps to a huge u32 (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.

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

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.

  1. Index arithmetic β€” mpr_mapping.c:1637-1638: c map_idx = et_entry->start_index + phy_change->slot - et_entry->start_slot; Types: map_idx u32 (:1579); phy_change->slot uint16_t (mpr_mapping.h:48); et_entry->start_slot u16 (mprvar.h:148); et_entry->start_index u32 (mprvar.h:143). slot comes from the SAS Device Page 0; start_slot from the Enclosure Status Change event β€” both firmware-controlled.

  2. Unguarded sink β€” mpr_mapping.c:1639: c mt_entry = &sc->mapping_table[map_idx]; /* no bounds check */ mapping_table has max_devices entries (:2140).

  3. Identical twin at :1894-1896 in _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_slot exceeds max_devices β†’ write past the allocation into the adjacent slab object.
  • underflow (Case B): slot < start_slot β†’ the unsigned result wraps to a value near 0xFFFFFFFF β†’ wild out-of-slab pointer write (in-kernel: panic / wild write; in userspace: SIGSEGV).

Primitive characterization

  • Write size: one struct dev_mapping_table entry (32 bytes), attacker-shaped (physical_id, dev_handle, device_info).
  • Target: mapping_table slab adjacency, or β€” in the underflow case β€” a near-arbitrary kernel address (controlled only by start_index/slot/start_slot magnitude).

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_testable

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