# 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`:

```c
    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

```sh
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)`.
