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

mrsas_get_pd_list indexes local_pd_list with unchecked firmware deviceId (OOB write)

Summary

mrsas_get_pd_list at mrsas.c:3310-3312: sc->local_pd_list[pd_addr->deviceId].tid/driveType/driveState with deviceId from firmware u16 (0-65535). local_pd_list is MRSAS_MAX_PD=256 entries (mrsas.h:2417). deviceId>=256 writes 3 fields past array into ld_ids/ev_tq/reset_flags/load_balance_info. Guard at :3307 only bounds count<256, not deviceId value. Triggered at attach and re-triggered on PD_INSERTED/PD_REMOVED AEN. Malicious/buggy HBA. Fix: check deviceId<MRSAS_MAX_PD.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1243 Β· 8 files
FileTypeDescriptionSize
fix.diff suggested-fix bounds-check deviceId (< MRSAS_MAX_PD) before indexing local_pd_list; skip out-of-range 1.3 KB view raw
VERDICT.md verdict source-level trace of OOB write into softc (ev_tq ptr) + dead-code analysis 3.3 KB ↓ raw
build.sh build-script applies fix, builds single-fix kernel 772 B view raw
run.sh run-script explains no-trigger on this guest 594 B view raw
env.txt environment uname, cc version, PCI inventory 553 B view raw
build.log build-log kernel build log excerpt proving -Werror clean compile of patched source 9.2 KB view 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
VERDICT.md verdict source-level trace of OOB write into softc (ev_tq ptr) + dead-code analysis
↓ download raw

DF-1243 β€” mrsas_get_pd_list unchecked deviceId (OOB write into softc)

Verdict: NOT REPRODUCED (dead code at runtime β€” no hardware)

Mechanism (source-level, confirmed real)

mrsas_get_pd_list() at sys/dev/raid/mrsas/mrsas.c:3270 queries the firmware for the physical-drive list and populates sc->local_pd_list[deviceId]. The deviceId is firmware-supplied and unchecked against the array bounds:

// line 3307-3314
if (retcode == 0 && pd_list_mem->count < pd_count) {   // checks count, NOT deviceId
    memset(sc->local_pd_list, 0, MRSAS_MAX_PD * sizeof(struct mrsas_pd_list));
    for (pd_index = 0; pd_index < pd_list_mem->count; pd_index++) {
        sc->local_pd_list[pd_addr->deviceId].tid = pd_addr->deviceId;
        sc->local_pd_list[pd_addr->deviceId].driveType = pd_addr->scsiDevType;
        sc->local_pd_list[pd_addr->deviceId].driveState = MR_PD_STATE_SYSTEM;
        pd_addr++;
    }
}

deviceId is u_int16_t (0–65535, mrsas.h:1351), but local_pd_list has only MRSAS_MAX_PD = 256 entries (mrsas.h:2417, 1830). The guard at line 3307 only bounds the count (< 256), not the deviceId values.

The softc layout after local_pd_list[256] (mrsas.h:2417-2424):

struct mrsas_pd_list local_pd_list[256];   // <-- the array
u_int8_t          ld_ids[MRSAS_MAX_LD];    // overwritten
struct taskqueue  *ev_tq;                  // <-- KERNEL FUNCTION POINTER
struct task       ev_task;
u_int32_t         CurLdCount;
u_int64_t         reset_flags;
LD_LOAD_BALANCE_INFO load_balance_info[MAX_LOGICAL_DRIVES];

A deviceId >= 256 writes .tid, .driveType, .driveState (3 fields of a struct mrsas_pd_list) past the array into ld_ids, and for large deviceId values, potentially into ev_tq β€” a kernel taskqueue pointer. Corrupting a function pointer and triggering the event task could redirect kernel execution.

The bug is real in source. A malicious firmware can set deviceId to any u16 value, causing an OOB write of 3 fields per entry past the 256-entry array, corrupting adjacent softc fields including a taskqueue pointer. Triggered at attach and re-triggered on PD_INSERTION events.

Why it cannot reproduce on this guest

Same as DF-1242: mrsas is in GENERIC but no LSI MegaRAID SAS hardware on the QEMU guest β†’ driver never attaches β†’ mrsas_get_pd_list (called from mrsas_init_adapter during attach) is never reached.

Valid hard blocker: dead/unreachable at runtime. Threat model is malicious firmware or a malicious PCIe device on a real MegaRAID SAS system.

Fix

fix.diff adds if (pd_addr->deviceId >= MRSAS_MAX_PD) { skip; continue; } inside the loop, so out-of-range deviceIds are logged and skipped rather than writing OOB. Validated by a successful single-fix kernel build (make -j6 nativekernel rc=0).

Impact

  • On this guest: none (driver never attaches, no hardware).
  • On a real system: OOB write of 3 fields per malicious PD entry past local_pd_list[256], corrupting ld_ids, ev_tq (taskqueue pointer), reset_flags, load_balance_info. From malicious firmware this is kernel memory corruption in the RAID driver β€” a crash (DoS) from buggy firmware, or a controlled corruption primitive (including potential function-pointer overwrite of ev_tq) from a malicious PCIe device. Triggered at attach and on PD events.

Fix verification

not_testable

compile validated

module/kernel build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. mrsas_get_pd_list deviceId no bounds vs [256] -> softc OOB write. mrsas in GENERIC, no MegaRAID HW.