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

mpssas_ir_shutdown scans [start_idx, end_idx) over an inclusive IR map range: the last volume slot is never checked, so its SYSTEM_SHUTDOWN_INITIATED action (cache flush) is skipped

Summary

end_idx is computed as the INCLUSIVE last slot (same formula consumed inclusively everywhere else in the mapping layer) but the shutdown loop uses id < end_idx - a volume mapped into the final slot is never seen and the RAID shutdown cache-flush action is never sent: unflushed volume write cache at poweroff (dirty-shutdown data loss). Arithmetic verified on guest. One-token <= fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4561 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.3 KB ↓ raw
fix.diff β€” 1.0 KB view raw
VERDICT.md β€” 1.7 KB ↓ raw
verdict.json β€” 2.5 KB view raw
../DF-4560/run.log β€” 1.7 KB view raw

DF-4561 β€” mpssas_ir_shutdown skips the last IR volume slot (off-by-one)

File: sys/dev/raid/mps/mps_sas_lsi.c:902-911 Severity: Low (data integrity at shutdown; no memory unsafety) Status: untested at driver level (needs an LSI IR HBA to exercise the shipped path); the loop-coverage arithmetic is deterministic and was executed on the guest (tail of findings/poc/DF-4560/run.log).

What the bug is

end_idx = start_idx + sc->max_volumes - 1 is the LAST slot of the IR map range β€” the exact formula of _mapping_get_ir_maprange() (mps_mapping.c:234), whose consumers scan it inclusively (map_idx <= end_idx, mps_mapping.c:366). The shutdown scan however uses

for (id = start_idx; id < end_idx; id++)

and therefore visits only max_volumes - 1 slots. A volume mapped into the final slot of the IR range is never seen, found_volume stays 0, and MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED is never sent to the IOC: the volume's onboard write cache is not flushed at poweroff β†’ dirty shutdown / potential data loss on the last writes.

Proof (arithmetic, executed on the guest, see DF-4560/run.log tail)

IR range [256..263] (8 slots); loop visits 7, last visited 262
-> slot 263 (the LAST volume slot) NEVER checked

Fix

One character: id < end_idx β†’ id <= end_idx (see fix.diff).

VERDICT.md
↓ download raw

DF-4561 VERDICT β€” untested (no HBA), Low, deterministic off-by-one proven by source + arithmetic

Bottom line

mpssas_ir_shutdown() scans id ∈ [start_idx, end_idx) while the IR map range it is scanning is [start_idx, end_idx] inclusive (sys/dev/raid/mps/mps_sas_lsi.c:902 end_idx = start_idx + sc->max_volumes - 1, loop at :904 id < end_idx; compare the inclusive consumers of the identical formula in sys/dev/raid/mps/mps_mapping.c:234 and :366). The final IR slot is never examined, so a volume mapped there never receives the SYSTEM_SHUTDOWN_INITIATED RAID action β†’ unflushed volume write cache at poweroff.

Why untested

The shipped path requires an mps controller with IR firmware (the QEMU guest has none). The defect is a pure loop-bound arithmetic error with no environmental dependence; the coverage count (visits 7 of 8 slots, last visited = end_idx-1, slot end_idx never checked) was computed and printed on the guest (tail of findings/poc/DF-4560/run.log). No runtime behavior beyond that arithmetic exists to observe without hardware, so the honest status is untested rather than reproduced.

Impact

Data integrity only (dirty shutdown of the last IR volume slot); no memory unsafety, no information flow. Low.

Fix

id <= end_idx (fix.diff β€” one-token change, matches the inclusive semantics used everywhere else the formula appears).

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

not_testable
baseline no→ patch + rebuild →patched clean

Pure one-token loop-bound change (fix.diff); arithmetic self-evident β€” with id <= end_idx the scan visits exactly max_volumes slots.

fix.diff; coverage arithmetic in findings/poc/DF-4560/run.log.
↓ fix.diffper-fix-DF-4561

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4560/run.log tail ('IR range [256..263] (8 slots); loop visits 7, last visited 262 -> slot 263 ... NEVER checked'); fix.diff (one-token change); VERDICT.md source trace.

PoC changes

No standalone PoC: no runtime behavior exists beyond the loop arithmetic without hardware; coverage computation folded into the DF-4560 harness run on the guest.

Verified recommended fix

Change the loop bound at mps_sas_lsi.c:904 from id < end_idx to id <= end_idx.

Verdict

UNTESTED at the driver level (requires an LSI IR HBA; the QEMU guest has none) β€” but the defect is deterministic loop arithmetic proven from source: mpssas_ir_shutdown scans id in [start_idx, end_idx) (mps_sas_lsi.c:904) while end_idx = start_idx + max_volumes - 1 (mps_sas_lsi.c:902) is the INCLUSIVE last slot of the IR map range (identical formula in _mapping_get_ir_maprange, mps_mapping.c:234, consumed inclusively at mps_mapping.c:366). Only max_volumes-1 slots are checked; a volume in the final slot is never seen, found_volume stays 0, and MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED is never sent β€” unflushed volume write cache at poweroff. Coverage arithmetic executed on the guest (tail of findings/poc/DF-4560/run.log): range [256..263], loop visits 7, slot 263 never checked. Data-integrity impact only; rated Low.