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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4331 Β· 14 files
FileTypeDescriptionSize
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/)
VERDICT.md
↓ download raw

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched 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)
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

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.