sassc->targets[] sized facts->MaxTargets but IR volume mapping IDs run to MaxTargets+MaxVolumes-1: mpssas_volume_add and the IR-config REMOVED branch write out of bounds (fw-controlled u64 at fixed offset)
Summary
The targets array is allocated with MaxTargets entries but the mapping layer legitimately assigns volume IDs up to MaxTargets+MaxVolumes-1; with the supported HIGH IRVolumeMappingFlags config every RAID volume ID lands past the end - writes include tid, fw u16 handle, an 8-byte fw-controlled wwid, and list-head NULL pairs. find_target_by_handle scanning only [0,MaxTargets) corroborates the divergence (the already-added check can never see a volume). Fires during normal discovery on HIGH-configured IR controllers - dirty-corruption reliability bug - or with fw-chosen IDs/payload in the attacker-controls-the-card model. Guard-page harness verified on guest: first volume write faults exactly at the boundary, 416 bytes clobbered past the end with the fw u64 at +51208; fixed-model arm clean. Size-for-full-ID-space fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4559 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df4559_harness.c | β | 11.3 KB | view raw | |
| build.sh | β | 94 B | view raw | |
| run.sh | β | 89 B | view raw | |
| build.log | β | 552 B | view raw | |
| run.log | β | 2.2 KB | view raw | |
| run.2.log | β | 2.2 KB | view raw | |
| env.txt | β | 230 B | view raw | |
| fix.diff | β | 2.5 KB | view raw | |
| VERDICT.md | β | 4.6 KB | β raw | |
| verdict.json | β | 4.6 KB | view raw |
DF-4559 VERDICT β reproduced (harness), High, OOB heap write in mps IR volume registration
Bottom line
sassc->targets[] is allocated with sc->facts->MaxTargets entries
(sys/dev/raid/mps/mps_sas.c:696-697) while the mapping layer legitimately
hands out target IDs up to sc->max_devices - 1 =
facts->MaxTargets + facts->MaxVolumes - 1 (sys/dev/raid/mps/mps_mapping.c:1733-1734,
:232-234). In HIGH IR-volume-mapping mode every RAID volume ID indexes
past the end of the array. Both RAID-volume registration sinks live in the
file under audit and write there: mpssas_volume_add()
(sys/dev/raid/mps/mps_sas_lsi.c:847-853: tid, fw u16 handle,
fw-controlled u64 devname = wwid, TAILQ_INIT/SLIST_INIT NULL
pointer pairs) and the IR CONFIG CHANGE REMOVED branch
(sys/dev/raid/mps/mps_sas_lsi.c:310-316: six zeroing writes), plus the
OOB targetid = targ - sassc->targets computation fed into CAM
(sys/dev/raid/mps/mps_sas.c:285 via mpssas_rescan_target from
mps_sas_lsi.c:317/:855).
Why it reproduces (evidence)
Guard-page harness on the DragonFly 6.5-DEVELOPMENT guest (run.log, run.2.log β two consecutive runs, identical):
- voladd-HIGH: the write sequence of mps_sas_lsi.c:848-853 at
id = max_devices - max_volumes = MaxTargetsSIGSEGVs on the guard page β the write begins at byten*sizeof(mpssas_target)of ann*sizeof(mpssas_target)-byte allocation: unambiguously past the end. - removed-HIGH: zeroing writes of :311-316 at
id = max_devices-1fault 1600 bytes past the end. - voladd-LOW control: clean β proves the trigger is exactly the ID-range mismatch, not the write sequence.
- Canary: 416 bytes corrupted past a 51200-byte allocation
([+51200..+52679]); readback of
devnameat +51208 returns the fw-supplied u64 verbatim β the controlled-write primitive is real.
Threat model / reachability
- The gate is IOC page 8
IRVolumeMappingFlagsselecting HIGH volume mapping plus any IR volume event (volume created / comes online (mps_sas_lsi.c:288/:406), or removed (:296-317)). That is a supported, operator-selectable configuration on LSI IR firmware β the corruption fires during normal discovery on such boxes, no malicious input needed. - In the malicious-firmware threat model (same one DF-1696/1697 use), the
fw fully controls the ID (mapping table) and the 8-byte
wwidpayload. - What stops short of uid=0: the write content is constrained (fixed
offsets inside
struct mpssas_target, value set = {bounded id, fw u16 handle, fw u64 wwid, NULLs, zeros}) and the overflow distance isk*sizeof(mpssas_target)for k < MaxVolumes β adjacent-heap corruption of whatever follows the targets allocation, panic-grade in practice. The guest has no mps HBA, so an end-to-end in-driver escalation chain cannot be demonstrated on this hardware; per the DF-4363/DF-4013 precedent the primitive is proven by the byte-exact harness.
Root cause (path:line)
- sys/dev/raid/mps/mps_sas.c:696-697 β allocation sized
sc->facts->MaxTargets. - sys/dev/raid/mps/mps_mapping.c:1733-1734 β ID space is
MaxTargets + MaxVolumes(sc->max_devices). - sys/dev/raid/mps/mps_mapping.c:232-234 β HIGH volume mapping puts volume IDs at the top of that space.
- sys/dev/raid/mps/mps_sas_lsi.c:847-853 and :310-317 β the OOB sinks.
- sys/dev/raid/mps/mps_sas.c:181 β
find_target_by_handlescanning only[0, MaxTargets)corroborates the divergence (volumes are invisible to the "already added" check at mps_sas_lsi.c:400).
Fix validation
fix.diff sizes sassc->targets with sassc->maxtargets =
facts->MaxTargets + facts->MaxVolumes and uses it for the by-handle scan.
Harness arm voladd-HIGH-FIXED re-runs the identical write sequence
against the fixed sizing: clean exit, zero canary clobber β the
previously-observed OOB write is gone (baseline vs patched in run.log).
Notes
- The informational harness arm also documents the
max_devicesu16 truncation corner (MaxTargets=65535/MaxVolumes=255 βmax_devices=254βstart_idxwraps to 0xFFFFFFFF at mps_sas_lsi.c:901): malicious-fw only, wildmapping_table[0xffffffff]read at shutdown; folded into this pack's notes rather than filed separately (absurd facts values). - Guest dirtied only with /root harness binaries; guest reset (with-src) performed after the run.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedValidated with the harness fixed-model arm (voladd-HIGH-FIXED): identical write sequence against MaxTargets+MaxVolumes sizing stays in bounds and exits clean; baseline sizing SIGSEGVs. fix.diff not applied to the read-only sys/ tree; kernel-build validation not meaningful for a harness-verified HBA driver finding (no mps controller in the guest).
run.log lines: '[voladd-HIGH ] -> KILLED by SIGSEGV ... (OOB write confirmed)' vs '[voladd-HIGH-FIXED] -> clean exit (all writes in bounds)'; fix.diff in pack.
Confirmed kernel references
Detail
Exploit chain
fw/IR config: pg8 IRVolumeMappingFlags=HIGH + any IR volume event (created/online at mps_sas_lsi.c:288/:406, removed at :296-317) -> mapping assigns volume id >= MaxTargets -> &sassc->targets[id] (mps_sas_lsi.c:847/:310) OOB heap writes incl. fw-controlled 8-byte devname=wwid at fixed offset +8 within the slot, 200-byte slots, up to MaxVolumes slots past the end -> adjacent-kernel-heap corruption (panic-grade; controlled-content write primitive proven by canary readback).
Evidence (decisive lines)
run.log/run.2.log: '[voladd-HIGH ] KILLED by SIGSEGV ... (OOB write confirmed)', '[voladd-LOW ] clean exit', '[removed-HIGH] KILLED by SIGSEGV', '[voladd-HIGH-FIXED] clean exit', 'canary result: 416 bytes clobbered past the allocation end, spanning [+51200 .. +52679]', 'devname readback ... MATCH: fw-controlled 8B OOB write'; VERDICT.md has the full path:line trace.
PoC changes
No seed PoC existed (pass-2 new finding). Harness written from scratch: faithful x86-64 replica of struct mpssas_target (mps_sas.h:40-79, sizeof=200, offsets printed), replica of the alloc (:697), ID space (:1734), IR maprange (:232-234) and the verbatim write sequences of :848-853 / :311-316; guard-page + canary + fixed-model arms; fork()ed scenarios.
Verified recommended fix
Size sassc->targets for the full mapping ID space: sassc->maxtargets = facts->MaxTargets + facts->MaxVolumes used in the kmalloc (mps_sas.c:696-697) and in mpssas_find_target_by_handle's scan bound (mps_sas.c:181).
Verdict
REPRODUCED (harness; guest has no LSI mps HBA β same methodology as DF-4363/DF-1696/DF-1697). sassc->targets is allocated with facts->MaxTargets entries (mps_sas.c:696-697) while the mapping layer hands out target IDs up to max_devices-1 = MaxTargets+MaxVolumes-1 (mps_mapping.c:1733-1734); with IOC pg8 IRVolumeMappingFlags=HIGH the IR volume IDs are exactly [MaxTargets, MaxTargets+MaxVolumes-1] (mps_mapping.c:232-234) and mpssas_volume_add (mps_sas_lsi.c:847-853) writes tid/handle/devname(=fw-controlled u64 wwid)/TAILQ_INIT/SLIST_INIT at targets[id] β past the end of the allocation. Guard-page harness on the DragonFly guest: voladd-HIGH SIGSEGVs on the first volume write at byte nsizeof(mpssas_target) of an nsizeof allocation; removed-HIGH (zeroing branch :310-316) faults at the last slot; LOW-mapping control runs clean; canary run measures 416 bytes clobbered past a 51200-byte allocation spanning [+51200..+52679] with the fw-supplied u64 verified to land at +51208. Fixed-model arm (array sized MaxTargets+MaxVolumes per fix.diff) exits clean β bad behavior gone. Escalation ceiling honestly noted: constrained-content, fixed-offset overflow; end-to-end in-driver chain not demonstrable without the HBA.
No comments yet.