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)
PoC verification
Evidence pack
findings/poc/DF-3952 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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:
- MLX_COMMAND passthrough (root/operator):
mu_datasizeis only bounded byMAXPHYS(mlx.c:1873) β a 128 KB kmalloc'd buffer typically maps to ~32 physically-discontiguous 4 KB segments; older DAC960 firmware families reportme_max_sgof 17 or 33. 32 > 17 β panic, from an ordinary management ioctl on a perfectly functioning controller. - Disk I/O:
mlxd_attach()capssi_iosize_maxat(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_maxblkallowing 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).
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_sgis 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-2010inside thebus_dmamap_loadcallbackmlx_setup_dmamap()β a callback that cannot propagate an error, so the driver chosepanic(). - Reachable segment counts: MLX_COMMAND allows
mu_datasizeup 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_testablenot_testable: no Mylex hardware in guest
fix.diff
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.
No comments yet.