Unbounded NumPhysDisks/NumActiveVolumes loop counts read past IOC config-page trailing arrays
Summary
Loop terminators derived from device-supplied U8 count fields (vol_pg NumPhysDisks ioc_page3 NumPhysDisks ioc_page2 NumActiveVolumes) iterate trailing variable-length PhysDisk[]/RaidVolume[] arrays of IOC config pages without confirming count fits in allocated page buffer. Malicious/faulty IOC reporting count larger than page causes OOB reads of page buffer and combined with DF-2484 the unbounded indices read from OOB slots used for further corruption. Amplifies DF-2484. [VERIFIED pass2: reproduced - NumPhysDisks 3 with 0 trailing entries drives 3 OOB PhysDisk reads with OOB-fetched index writing raid_disks 3 flags guard word captured; PageLength-clamp fix A/B no smash]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2485 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.0 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| build.sh | β | 99 B | view raw | |
| run.log | β | 647 B | view raw | |
| env.txt | β | 963 B | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| manifest.json | β | 779 B | view raw | |
| verdict.json | β | 2.9 KB | view raw |
DF-2485 -- mpt_raid config-page count loops walk past the page allocation
Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_mpt (rig.c = harness, gen.sh = verbatim extractor, dfv_*_extract.h = generated production text). The decisive outputs below were captured live on the guest during the 2026-10-24 verification session; full session logs are summarized per run.
Build
sh build.sh # extracts verbatim driver text + builds the KLD
Run (guest, root)
kldload ./dfv_rig.ko
kldload dfv_mpt.ko; for i in 0 1 2 3; do sysctl kern.dfv_mpt_run=$i >/dev/null; sysctl -n kern.dfv_mpt_status; done
Expected
run.log: 3 refresh_disk walks, raid_disks-guard[70]=aaaaaaab (OOB write); announce 5 walks on OOB data; fixed: no smash.
Fix validation
fix.diff clamps all three counts to the page-header-derived maximum; note in diff: allocation-size validation belongs in the page-read path.
Verdict: reproduced / impact=limited_write (see verdict.json, VERDICT.md)
DF-2485 verdict -- mpt_raid config-page count loops walk past the page allocation
status: reproduced reproduced: 1 impact: limited_write confidence: certain attempts: 2 runtime_sec: 900
The verbatim mpt_refresh_raid_data and mpt_announce_vol iterate device-supplied counts (ioc_page3->NumPhysDisks :1401, ioc_page2->NumActiveVolumes :1432, vol_pg->NumPhysDisks :1149) over trailing arrays without checking the counts fit the page. The rig plants honest page headers (PageLength covering the header only) plus oversubscribed counts - exactly what a hostile IOC delivers - with the raid arrays in a single guarded allocation. Hostile page3 NumPhysDisks=3 (page holds 0 entries): the loop ran 3 times reading PhysDisk[0..2] past the page (0x03 canary), and the OOB-fetched PhysDiskNum indexed raid_disks[3] - one past the 2-entry array - whose flags |= MPT_RDF_REFERENCED|ACTIVE write landed in the guard (raid_disks-guard[70]=aaaaaaab captured). The announce loop (NumPhysDisks=5) similarly printed garbage flags read past the allocation (the OOB-read amplification arm of DF-2484). Control (honest counts) clean. Fix (clamp counts to the PageLength-derived maximum) A/B: no guard smash. HW-gated at runtime (no LSI MPT controller on the guest).
Evidence (decisive lines)
DF2485[HOSTILE]: page3 NumPhysDisks=3 (page holds 0 entries), page2 NumActiveVolumes=2 (page holds 0) DF2485: driver log: read_cfg_page(len=0) refresh_disk refresh_disk refresh_disk read_cfg_page(len=0) mpt_announce_disk D raid_disks-guard[70]=aaaaaaab DF2485: RESULT=COUNT-DRIVEN OOB (loops walked past the page allocation; OOB-fetched PhysDiskNum indexed raid_disks[] OOB) DF2485[announce]: 5 member lines printed from OOB PhysDisk[] reads (Quiesced/Was Non-Optimal/Offline/Other garbage) --- control --- DF2485[CONTROL]: ... RESULT=clean (honest counts) --- fixed source A/B --- DF2485: guard smashed=0 (clamped loops; no raid_disks OOB write)
Exploit chain / privesc assessment
malicious/faulty IOC (PCI trust boundary); OOB read of the page plus an IOC-indexed OOB |= write into raid_disks (limited write primitive per this findings scope; DF-2484 owns the far-index arm).
Fix + A/B
fix.diff clamps all three counts to the page-header-derived maximum; note in diff: allocation-size validation belongs in the page-read path.
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
fixedConfirmed kernel references
Detail
Exploit chain
malicious/faulty IOC (PCI trust boundary); OOB read of the page plus an IOC-indexed OOB |= write into raid_disks (limited write primitive per this findings scope; DF-2484 owns the far-index arm).
Evidence (decisive lines)
run.log: 3 refresh_disk walks, raid_disks-guard[70]=aaaaaaab (OOB write); announce 5 walks on OOB data; fixed: no smash.
PoC changes
rig needed honest PageLength values planted in the fabricated pages (first attempt left them zero, masking the fix effect); guard placed in the same allocation after the arrays.
Verified recommended fix
clamp NumPhysDisks/NumActiveVolumes/NumPhysDisks to the page header length
Verdict
The verbatim mpt_refresh_raid_data and mpt_announce_vol iterate device-supplied counts (ioc_page3->NumPhysDisks :1401, ioc_page2->NumActiveVolumes :1432, vol_pg->NumPhysDisks :1149) over trailing arrays without checking the counts fit the page. The rig plants honest page headers (PageLength covering the header only) plus oversubscribed counts - exactly what a hostile IOC delivers - with the raid arrays in a single guarded allocation. Hostile page3 NumPhysDisks=3 (page holds 0 entries): the loop ran 3 times reading PhysDisk[0..2] past the page (0x03 canary), and the OOB-fetched PhysDiskNum indexed raid_disks[3] - one past the 2-entry array - whose flags |= MPT_RDF_REFERENCED|ACTIVE write landed in the guard (raid_disks-guard[70]=aaaaaaab captured). The announce loop (NumPhysDisks=5) similarly printed garbage flags read past the allocation (the OOB-read amplification arm of DF-2484). Control (honest counts) clean. Fix (clamp counts to the PageLength-derived maximum) A/B: no guard smash. HW-gated at runtime (no LSI MPT controller on the guest).
No comments yet.