# DF-3928 verdict narrative

## Reproduced?
No (hardware-gated: no Mylex controller in the audit guest).  Source-level
certainty: certain — every link in the chain is static, with no dependency on
runtime state.

## Root cause chain (path:line)

1. `mly_get_controllerinfo()` (sys/dev/raid/mly/mly.c:744-780) receives the
   `struct mly_ioctl_getcontrollerinfo` blob DMA'd by the controller firmware.
   `physical_channels_present` and `virtual_channels_present` are `u_int8_t`
   (mlyreg.h:576-577) — no validation anywhere.
2. `mly_cam_attach()` (mly.c:1947-1961):
   ```c
   chn = sc->mly_controllerinfo->physical_channels_present;   /* 0..255 */
   for (i = 0; i < sc->mly_controllerinfo->virtual_channels_present; i++, chn++) {
       if ((sc->mly_cam_sim[chn] = cam_sim_alloc(...)) == NULL) {
   ```
   `mly_cam_sim` is `struct cam_sim *mly_cam_sim[MLY_MAX_CHANNELS]` with
   `MLY_MAX_CHANNELS == 6` (mlyvar.h:206, mlyreg.h:59).  With, e.g.,
   physical=0/virtual=255 the loop stores kernel heap pointers at
   `mly_cam_sim[6..254]` — up to 1992 bytes past the array, overrunning
   `mly_cam_channels`, `mly_task_complete`, `mly_qfrzn_cnt` and the entire
   remainder of the softc allocation into adjacent slab memory.  The physical
   loop (mly.c:1927-1941, gated on the `hw.mly.register_physical_channels`
   env var) can push writes up to index 509.
3. `sc->mly_cam_channels = physical + virtual` (mly.c:1967-1968) — up to 510.
4. `mly_scan_devices()` (mly.c:802-805):
   ```c
   for (bus = 0; bus < sc->mly_cam_channels; bus++)      /* 0..509 */
       if (MLY_BUS_IS_VALID(sc, bus))                    /* reads mly_cam_sim[bus] OOB */
           for (target = 0; target < MLY_MAX_TARGETS; target++)
               sc->mly_btl[bus][target].mb_flags = MLY_BTL_RESCAN;
   ```
   `mly_btl` is `[MLY_MAX_CHANNELS][MLY_MAX_TARGETS]` = [6][16] (mlyvar.h:206);
   the walk writes `sizeof(struct mly_btl)==36`-strided words of value 8 across
   up to 510*16 = 8160 entries ⇒ ~290KB past `mly_btl`, i.e. far beyond the
   ~30KB softc allocation.  `mly_periodic()` (mly.c:1378-1391) repeats the same
   walk every second; `MLY_BUS_IS_VALID` (mlyvar.h:286) itself reads
   `mly_cam_sim[bus]` out of bounds for bus ≥ 6.
5. `mly_rescan_btl()` (mly.c:817-868) then kfree/malloc/kmalloc-DMAs based on
   those OOB'd arrays.

## Threat model
Malicious or buggy PCIe controller (DMA'd firmware response), identical trust
boundary to the accepted DF-1290 (event channel/target/lun OOB write) — but
larger and at attach, and including pointer-valued writes (cam_sim pointers).

## Impact
Kernel heap corruption at attach: near-certain panic; with a groomed heap the
~4KB of kernel-pointer writes plus 290KB of strided flag writes are usable
corruption primitives (write-pointer-values / write-constant-8 patterns).

## Why not runtime-reproduced here
No mly controller exists in the audit guest (`pciconf -l` shows virtio only),
and there is no software path to make the driver fetch controllerinfo from
anything but the card.  Per the pass-1 family convention (DF-1289/DF-1290)
this is filed as not_reproduced with certain source confidence.

## Fix
Validate the sums against MLY_MAX_CHANNELS right after GETCONTROLLERINFO —
see fix.diff (returns EIO; attach fails cleanly).

## 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.
