WarpDrive validation accepts NumPhysDisks == 0: guaranteed integer divide-by-zero panic in mpssas_direct_drive_io on the first qualifying READ/WRITE
Summary
mps_wd_config_pages rejects NumPhysDisks > 8 and explicitly rejects stripe-size/block-size 0 but never rejects count 0 - a 0-disk RAID0 page sets DD_num_phys_disks = 0 and WD_valid_config = TRUE. Every qualifying READ/WRITE then reaches stripe_unit = physLBA / sc->DD_num_phys_disks -> #DE -> kernel panic on the first qualifying IO by any unprivileged user of the device. Corrupt/malicious controller firmware or NVRAM (0-disk RAID0 pages are a real volume-teardown-race shape). One-clause rejection fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3897 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| run.sh | β | 111 B | view raw | |
| run.log | β | 482 B | view raw | |
| env.txt | β | 274 B | view raw | |
| fix.diff | β | 710 B | view raw | |
| verdict.json | β | 2.3 KB | view raw | |
| manifest.json | β | 634 B | view raw |
DF-3897 β WarpDrive validation accepts NumPhysDisks == 0: divide-by-zero panic in mpssas_direct_drive_io
File: sys/dev/raid/mps/mps_sas.c (pass 2); the missing guard lives in
sys/dev/raid/mps/mps_config.c, the fatal sinks are in this file.
What
WarpDrive (direct-drive) configuration is enabled by
mps_wd_config_pages (sys/dev/raid/mps/mps_config.c), which validates the
RAID volume page 0 as:
if ((raid_vol_pg0->VolumeType != MPI2_RAID_VOL_TYPE_RAID0) ||
(raid_vol_pg0->NumPhysDisks > 8)) { /* mps_config.c:399-400 */
... "Direct Drive I/O will not be used" ... goto out;
}
...
sc->DD_num_phys_disks = raid_vol_pg0->NumPhysDisks; /* mps_config.c:412 */
...
sc->WD_valid_config = TRUE; /* mps_config.c:511 */
There is no NumPhysDisks == 0 rejection: a RAID0 page with zero
physical disks passes the > 8 check, DD_num_phys_disks becomes 0, and
WD_valid_config is set. Every subsequent READ/WRITE to the volume's
DevHandle enters mpssas_direct_drive_io (mps_sas.c:1760-1762), which
divides by it:
stripe_unit = physLBA / sc->DD_num_phys_disks; /* mps_sas.c:2190 (6-byte CDBs) */
stripe_unit = physLBA / sc->DD_num_phys_disks; /* mps_sas.c:2269 (10/16-byte, low LBA) */
stripe_unit = physLBA / sc->DD_num_phys_disks; /* mps_sas.c:2347 (16-byte, high LBA) */
An integer division by zero in kernel text on x86_64 raises #DE β
panic: integer divide by zero (or fatal trap) on the first qualifying
I/O β an unprivileged process merely has to read() or write() a file on
the wed volume-shaped device.
Contrast: the same function does reject stripe-size 0 and block-size 0
explicitally (mps_config.c:428-433, 447-452) β the disk-count zero case
was simply missed. (Note stripe_size==0 β the virtLBA &
(DD_stripe_size - 1) masks at mps_sas.c:2185/2263 also degrade, but the
div-by-zero fires first in the zero-disk case.)
Threat model
NumPhysDisks comes from the controller's own config-page DMA (firmware /
NVRAM-backed volume table), not from disk-supplied data β so this is a
malicious-or-corrupt-firmware / mis-flashed-WarpDrive scenario, or a
legitimate LSI firmware bug class (0-disk RAID0 pages have been observed
during volume teardown races). Hardware- and firmware-gated; hence Low /
speculative, but the sink is a guaranteed kernel panic.
Build/run
run.sh records the hardware gate.
Fix
Reject NumPhysDisks == 0 in the WarpDrive validation (mps_config.c:399-400). See fix.diff.
DF-3897 β VERDICT
Status: not_reproduced (hardware/firmware-gated) β sink certain from source, trigger speculative. Impact class: divide-by-zero kernel panic (DoS), Low.
Proof chain
sys/dev/raid/mps/mps_config.c:399-405β the only NumPhysDisks check is> 8;== 0passes. (VolumeType must be RAID0, which does not imply disk-count β₯ 1 to the parser.)sys/dev/raid/mps/mps_config.c:412βsc->DD_num_phys_disks = raid_vol_pg0->NumPhysDisks;(uint8_t, mpsvar.h:435); validation continues;sc->WD_valid_config = TRUEat mps_config.c:511 (the function bails toout:before this on every checked anomaly β stripe 0 at 428-433, block 0 at 447-452, multiple volumes at 481-486, bad PD pages at 495-507 β but not for count 0).sys/dev/raid/mps/mps_sas.c:1760-1762β every XPT_SCSI_IO on the adapter then callsmpssas_direct_drive_io(sassc, cm, ccb).sys/dev/raid/mps/mps_sas.c:2157-2191,2222-2277,2306-2391β for READ/WRITE 6/10/16 tosc->DD_dev_handle, once the LBA/stripe checks pass:stripe_unit = physLBA / sc->DD_num_phys_disks;at 2190, 2269,- With DD_num_phys_disks == 0 β #DE β kernel panic on first qualifying I/O by any unprivileged user of the device.
- Column note:
column = physLBA % DD_num_phys_disks(2191, 2271, 2349) indexesDD_column_map[column]β with the count check fixed to β₯ 1 the modulo bounds column < count β€ 8 = MPS_MAX_DISKS_IN_VOL, so that side is safe once this finding is fixed.
Why not reproduced
Needs a real LSI WarpDrive/SAS2 HBA reporting a 0-disk RAID0 volume page; the QEMU guest has none (env.txt/run.log). Speculative trigger, certain sink.
Fix validation
Not testable here; fix.diff adds the == 0 rejection beside the existing
> 8 check β one line, same style as the stripe/block zero guards.
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_testablefix.diff adds the one-line rejection; cannot be exercised on this guest (no SAS2 HBA).
findings/poc/DF-3897/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (path:line proof: unchecked count, three division sinks, guarded siblings), env.txt, run.sh + run.log (hardware gate), fix.diff (== 0 rejection in mps_config.c)
PoC changes
n/a β hardware/firmware-gated.
Verified recommended fix
Reject NumPhysDisks == 0 in mps_wd_config_pages beside the existing > 8 check.
Verdict
The WarpDrive validation in mps_wd_config_pages only rejects NumPhysDisks > 8 (sys/dev/raid/mps/mps_config.c:399-400) while explicitly rejecting stripe-size 0 and block-size 0 (mps_config.c:428-433, 447-452) β a RAID0 volume page with zero physical disks passes, sets DD_num_phys_disks = 0 (mps_config.c:412) and WD_valid_config = TRUE (mps_config.c:511). Every subsequent READ/WRITE to the volume DevHandle then reaches stripe_unit = physLBA / sc->DD_num_phys_disks in mpssas_direct_drive_io (mps_sas.c:2190, 2269, 2347) β integer divide by zero, kernel panic on the first qualifying I/O by any unprivileged user of the device. Trigger is firmware/NVRAM-gated (controller config page, observed-class 0-disk RAID0 during volume teardown), hence speculative; the sink is certain from source. Not reproduced: guest has no SAS2/WarpDrive HBA (env.txt).
No comments yet.