Spanset code divides by firmware fields that can be zero: mr_update_span_set quad->diff (map load) and get_arm/mr_spanset_get_phy_params spanRowSize (first FP IO) - kernel #DE panic
Summary
The quad-diff division at map-validation time has no zero check - unlike every IO-time consumer of the same field, which set div_error and bail. It runs at attach and on every AEN RAID-map sync: a map with any live quad diff==0 panics at boot (boot-persistent DoS). Additionally the spanset get_arm/phy_params divide by the fw spanRowSize for RAID 0/5/6 on the first FastPath IO - the non-spanset twin guards; the spanset copies never got the guards. VERIFIED LIVE on the guest: mode-2 probe -> 'Fatal trap 18: integer divide fault' at the compiled mega_div64_32 through the real entry point; patched module rejects with no panic. Shared validation fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4331 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fp_probe.c | β | 7.4 KB | view raw | |
| rename.h | β | 1.1 KB | view raw | |
| Makefile | β | 518 B | β download | |
| build.sh | β | 88 B | view raw | |
| fix.diff | β | 2.1 KB | view raw | |
| build.log | β | 13.8 KB | view raw | |
| run.log | β | 306 B | view raw | |
| panic.txt | β | 306 B | view raw | |
| serial_decisive.log | β | 2.6 KB | view raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| README.md | β | 1.9 KB | β raw | |
| verdict.json | β | 3.1 KB | view raw | |
| manifest.json | β | 773 B | view raw | |
| env.txt | β | 363 B | view raw |
DF-4331 β mrsas firmware-zero divisors β kernel #DE panic (map load + IO paths)
File: sys/dev/raid/mrsas/mrsas_fp.c
* mr_update_span_set:969 β mega_div64_32(..., quad->diff) with fw diff == 0
(fires at map validation time)
* get_arm:599 β mega_mod64(stripe, SPAN_ROW_SIZE(...)) with fw
spanRowSize == 0 (fires on first FP IO, RAID 0/5/6)
* mr_spanset_get_phy_params:653-654 β same SPAN_ROW_SIZE divisor for RAID 6
Same probe as DF-4330 (fp_probe.ko embedding the unmodified kernel
mrsas_fp.c).
Run (guest, root) β panic run
kldload /root/fp4330/fp_probe.ko sysctl hw.fp_probe_run=2 # quad[0].diff = 0
Expected result (see panic.txt / serial_decisive.log)
fp_probe: calling real MR_ValidateMapInfo() (spanDepth=1 quad[0].diff=0) ... Fatal trap 18: integer divide fault while in kernel mode instruction pointer = 0x8:0xffffffff82600cb7 kernel: type 18 trap, code=0 Stopped at probe_mr_update_span_set.part.0+0x1d7: divq %rcx,%eax
Trap 18 = x86 #DE. The identical code compiled into the kernel is what a real
adapter delivering quad.diff = 0 executes at attach (mrsas.c:1762) or at
any AEN map sync (mrsas.c:2961) β no user IO needed, the box panics while
bringing the controller up. Contrast: the IO-time paths do check
quad->diff == 0 (mrsas_fp.c:390-393, 1431-1434) and
MR_GetPhyParams checks rowSize/modFactor (:1359-1370), but the
spanset twins (969, 599, 653-654) have no zero guards.
Fix validation
fix.diff (shared with DF-4330) rejects any map with a live
quad->diff == 0 (or spanRowSize == 0 on RAID 0/5/6) inside
MR_ValidateMapInfo, before mr_update_span_set runs:
kldload /root/fp4330_fixed/fp_probe.ko
sysctl hw.fp_probe_run=2 # -> "invalid RAID map geometry", returned 1,
# NO panic, guest stays up (fix_run.log in DF-4330/)
DF-4331 β VERDICT
Status: reproduced (certain). Impact: kernel panic (trap 18, #DE).
Root cause
mr_update_span_set divides by the firmware-supplied quad diff
(mrsas_fp.c:969) with no zero check, unlike every IO-time consumer of the
same field (mrsas_fp.c:390-393 mr_spanset_get_span_block,
:1431-1434 MR_GetSpanBlock β both explicitly set *div_error = 1 and
bail). The function runs at RAID-map validation time, i.e. at adapter attach
(mrsas.c:1762, gated only on fw supportUnevenSpans) and again on every AEN
map update (mrsas.c:2961). Two more unguarded fw divisors exist on the
UnevenSpan IO path: get_arm (:599, mega_mod64(stripe, SPAN_ROW_SIZE(...))
β spanRowSize fw u_int8_t) and mr_spanset_get_phy_params
(:653-654, RAID-6 branch, same SPAN_ROW_SIZE divisor). The non-spanset
twin MR_GetPhyParams does guard rowSize == 0 and modFactor == 0
(:1359-1370) β the guards were simply never added to the spanset copy.
Reproduction
Same probe as DF-4330 (unmodified guest mrsas_fp.c, symbol-renamed,
MR_ValidateMapInfo entry). Map crafted with quad[0].diff = 0:
fp_probe: calling real MR_ValidateMapInfo() (spanDepth=1 quad[0].diff=0) ... Fatal trap 18: integer divide fault while in kernel mode kernel: type 18 trap, code=0 Stopped at probe_mr_update_span_set.part.0+0x1d7: divq %rcx,%eax
(panic.txt, serial_decisive.log β serial console; guest wedged in ddb,
vm.sh status β down right after.) The faulting instruction is the compiled
mega_div64_32(((quad->logEnd - quad->logStart) + quad->diff), quad->diff)
at source line 969. On real hardware a firmware image (or PCIe DMA attack)
delivering diff = 0 for any live quad bricks the machine at boot or on the
first config-change AEN β a persistent boot-time DoS.
Fix validation
fix.diff (shared with DF-4330) validates every live quad diff != 0 (and
spanRowSize != 0 for RAID 0/5/6) inside MR_ValidateMapInfo before
mr_update_span_set is called. Patched probe, mode 2:
nexus0: invalid RAID map geometry (ld/span/quad), rejecting map,
MR_ValidateMapInfo returned 1, no panic, guest stays up
(../DF-4330/fix_run.log); benign mode-0 map still accepted (no regression).
Exploit chain
None β pure availability. (DoS bucket.)
Artifacts
panic.txt, run.log (panic run), serial_decisive.log, shared probe
sources (fp_probe.c, rename.h, Makefile, build.sh), fix.diff,
build.log.
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
fixedPatched mrsas_fp.c rejects the diff=0 map at validation (returned 1, console message), no #DE, guest alive; benign map still accepted - regression-free
DF-4330/fix_run.log: mode 2 block 'invalid RAID map geometry (ld/span/quad), rejecting map' + 'MR_ValidateMapInfo returned 1' + 'guard integrity: 0/4096' with the guest remaining responsive (phases completed, kldunload succeeded)
Confirmed kernel references
Detail
Exploit chain
fw map with quad.diff=0 -> MR_ValidateMapInfo passes totalSize check -> mr_update_span_set mega_div64_32(...,0) -> #DE -> kernel panic in ddb, all CPUs stopped
Evidence (decisive lines)
panic.txt / run.log: 'Fatal trap 18: integer divide fault while in kernel mode' + 'Stopped at probe_mr_update_span_set.part.0+0x1d7: divq %rcx,%eax'; serial_decisive.log: full console record; DF-4330/fix_run.log: patched build rejects the same map with no panic
PoC changes
same probe as DF-4330; mode 2 crafts quad[0].diff=0 with consistent totalSize so the map passes the driver's only check
Verified recommended fix
Reject maps with any live quad diff==0 or spanRowSize==0 on RAID 0/5/6 in MR_ValidateMapInfo (fix.diff, shared with DF-4330); alternatively mirror the existing MR_GetPhyParams zero guards at mrsas_fp.c:969/599/653
Verdict
REPRODUCED: kernel Fatal trap 18 (integer divide fault, divq) inside the real mr_update_span_set (mrsas_fp.c:969) reached through the real MR_ValidateMapInfo call path when the firmware map carries quad.diff==0. On hardware this fires at adapter attach (mrsas.c:1762) or any AEN map sync (mrsas.c:2961) - a boot-persistent DoS from a buggy/malicious MegaRAID firmware. Two sibling unguarded fw divisors on the IO path (get_arm:599 and mr_spanset_get_phy_params:653-654 via spanRowSize==0) are fixed by the same validation.
No comments yet.