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

mlx_setup_dmamap() panics when DMA segments exceed firmware me_max_sg, while the buffer tag is hard-coded to MLX_NSEG=64 and MLX_COMMAND passthrough is uncapped

Summary

The buffer DMA tag is created with nsegments=64 at attach, before ENQUIRY2, but mlx_setup_dmamap panic()s if nsegments > firmware me_max_sg - a bus_dmamap_load callback that cannot fail, so the driver chose panic. MLX_COMMAND bounds mu_datasize only by MAXPHYS: a 128KB buffer maps to ~32 segments > me_max_sg=17 (older firmware) => kernel panic from a stock management ioctl. Disk I/O mostly capped by si_iosize_max with the same panic as the only backstop. Bound-the-passthrough fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3952 Β· 9 files
FileTypeDescriptionSize
README.md doc bug + reachability + fix 2.2 KB ↓ raw
VERDICT.md verdict source proof 2.0 KB ↓ raw
mlx_ioctl_trigger.c trigger bigbuf mode: 128KB passthrough > me_max_sg -> panic 4.6 KB view raw
build.sh script build 108 B view raw
run.sh script run 117 B view raw
build.log log guest compile 356 B view raw
run.log log guest run (ENOENT, no HBA) 217 B view raw
env.txt env guest uname 185 B view raw
fix.diff suggested-fix see README 861 B view raw
README.md doc bug + reachability + fix
↓ download raw

DF-3952 β€” mlx_setup_dmamap(): kernel panic when DMA segments exceed firmware me_max_sg (tag built with fixed MLX_NSEG=64)

Severity: Low Β· CWE-248/478 (panic as error handling) Β· hardware-gated (DAC960 firmware reporting me_max_sg < 33)

Bug

sys/dev/raid/mlx/mlx.c:2009-2010:

    if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
    panic("MLX: too many s/g segments (%d, max %d)", nsegments,
          sc->mlx_enq2->me_max_sg);

is the only guard between the DMA tag's segment budget and the firmware's real limit β€” but the buffer DMA tag is created with a hard-coded nsegments = MLX_NSEG (= 64) at attach time (mlx.c:363-370: bus_dma_tag_create(..., MAXBSIZE, MLX_NSEG, ...)), before ENQUIRY2 has reported the controller's actual me_max_sg (u_int16_t, firmware-controlled, mlxreg.h:291).

Paths that can legally produce more segments than me_max_sg:

  1. MLX_COMMAND passthrough (root/operator): mu_datasize is only bounded by MAXPHYS (mlx.c:1873) β€” a 128 KB kmalloc'd buffer typically maps to ~32 physically-discontiguous 4 KB segments; older DAC960 firmware families report me_max_sg of 17 or 33. 32 > 17 β†’ panic, from an ordinary management ioctl on a perfectly functioning controller.
  2. Disk I/O: mlxd_attach() caps si_iosize_max at (me_max_sg - 1) * PAGE_SIZE (mlx_disk.c:268), which mostly keeps disk bios within budget β€” but the cap is derived from the same firmware value; any mismatch (e.g. me_maxblk allowing larger, boundary splits) falls through to the same panic instead of an I/O error.

Panic instead of error return = trivially-triggerable kernel DoS on the affected configurations; no memory corruption.

Build / Run / Expected

cc -O2 -Wall -o mlx_ioctl_trigger mlx_ioctl_trigger.c
./mlx_ioctl_trigger bigbuf /dev/mlx0    # 128KB DCDB passthrough on me_max_sg<=17 fw

Expected on affected hardware: panic: MLX: too many s/g segments (N, max M) from mlx_setup_dmamap. On this guest: ENOENT (no HBA) β€” hardware-gated.

Fix

Bound the passthrough buffer by the firmware's own S/G budget before mapping (fix.diff); long-term the tag should be created after ENQUIRY2 with nsegments = imin(MLX_NSEG, me_max_sg).

VERDICT.md verdict source proof
↓ download raw

DF-3952 β€” Verification Verdict

Status: inconclusive (hardware-gated; source-certain) Impact demonstrated: none (expected: kernel panic "MLX: too many s/g segments") Confidence: likely (needs a DAC960 reporting me_max_sg < ~33; code path certain)

Source proof

  • Buffer DMA tag hard-codes nsegments=MLX_NSEG=64 at attach: sys/dev/raid/mlx/mlx.c:363-370 (bus_dma_tag_create(..., MAXBSIZE, MLX_NSEG, ...)), created before ENQUIRY2 runs (mlx.c:395).
  • me_max_sg is firmware-controlled (sys/dev/raid/mlx/mlxreg.h:291, u_int16_t inside the DMA'd ENQUIRY2 response).
  • The only reconciliation is the panic at mlx.c:2009-2010 inside the bus_dmamap_load callback mlx_setup_dmamap() β€” a callback that cannot propagate an error, so the driver chose panic().
  • Reachable segment counts: MLX_COMMAND allows mu_datasize up to MAXPHYS (mlx.c:1872-1874); a 128 KB kmalloc buffer maps to up to ~32 segments on 4 KB pages; disk bios are capped at (me_max_sg-1)*PAGE_SIZE (mlx_disk.c:262-269) which bounds the ordinary-I/O case β€” the passthrough case is uncapped relative to me_max_sg.
  • Historical DAC960 firmware reports me_max_sg of 17/33/65 (Mylex DAC960 PG/ PJ/PD families) β€” 17 < 32 β‡’ panic reachable from a stock management ioctl.

Impact: kernel panic = availability only; operator/root gated (0600 root:operator device). Low.

Guest result

Trigger compile-tested (BUILD_RC=0, DragonFly 6.5-DEVELOPMENT); runtime ENOENT β€” no Mylex hardware. Honest: inconclusive.

Fix validation

fix.diff bounds mu_datasize by the firmware budget. Not runtime-testable here β†’ fix_status: not_testable.

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

not_testable: no Mylex hardware in guest

fix.diff
↓ fix.diffper-fix-DF-3952

Confirmed kernel references

Detail

Exploit chain

operator/root on a me_max_sg<33 controller: ioctl(MLX_COMMAND, 128KB DCDB) -> >me_max_sg DMA segments -> panic('MLX: too many s/g segments')

Evidence (decisive lines)

README/VERDICT cite mlx.c:363-370,1873,2009-2010 + mlxreg.h:291 + mlx_disk.c:268; mlx_ioctl_trigger.c (bigbuf mode); build.log/run.log; fix.diff bounds mu_datasize by firmware budget

PoC changes

no seed; self-contained trigger

Verified recommended fix

Reject mu_datasize > (me_max_sg-1)*PAGE_SIZE in mlx_user_command; long-term create the tag after ENQUIRY2 with imin(MLX_NSEG, me_max_sg) segments.

Verdict

Source-certain panic-by-design, hardware-dependent reachability: the buffer DMA tag hard-codes nsegments=MLX_NSEG=64 at attach (mlx.c:363-370, before ENQUIRY2), while mlx_setup_dmamap panics if segments exceed the firmware-reported me_max_sg (mlx.c:2009-2010; u16, mlxreg.h:291). MLX_COMMAND bounds mu_datasize only by MAXPHYS (mlx.c:1873): a 128KB kmalloc buffer ~32 segments > me_max_sg=17 (older DAC960 firmware) => panic from a management ioctl; disk I/O is mostly capped by si_iosize_max=(me_max_sg-1)*PAGE_SIZE (mlx_disk.c:268) but the same panic is the only backstop there. Runtime inconclusive: no DAC960 in guest.