# DF-2074 — mlxd_strategy dereferences stale/uninitialized bio_driver_info

## Verdict
REPRODUCED (source-only confirmation; HW-gated — no Mylex DAC960/acceleRAID
adapter on guest).

## Severity / impact
Low. Realistic ceiling: local DoS. When `bio_driver_info` is NULL every I/O
fails EINVAL (driver non-functional). When it holds a stale non-NULL value
(recycled from a prior `struct bio` / pbuf), `sc->mlxd_drive` (offset ~24) is a
wild pointer deref → kernel page-fault panic. Requires operator/root access to
`/dev/mlxd*` (`AV:L/AC:H/PR:L`). No escalation primitive — pure DoS + a stale-
pointer hardening gap.

## Mechanism (cited path confirmed line-by-line)
`mlxd_strategy(ap)` (`sys/dev/raid/mlx/mlx_disk.c:156-190`):
1. `bio = ap->a_bio; bp = bio->bio_buf;` (:159-160).
2. **`sc = (struct mlxd_softc *)bio->bio_driver_info;` at :161 — reads a field
   that NOTHING on the I/O path populates.**
3. `if (sc == NULL)` → EINVAL (good for the NULL case).
4. Otherwise: `sc->mlxd_drive->ms_state` (:173), `devstat_start_transaction`,
   `mlx_submit_bio(sc->mlxd_controller, bio)` (:180) — all deref the stale `sc`.

Where `bio_driver_info` should come from but doesn't:
- `push_bio` (`sys/kern/vfs_bio.c:768-788`) inits only `bio_prev`, `bio_buf`,
  `bio_offset`, `bio_done`, `bio_next` — explicitly NOT `bio_driver_info`.
- `mlx_submit_bio` (`sys/dev/raid/mlx/mlx.c:683-693`) does not set it.
- `dev_dstrategy` / `physio` paths do not set it for this driver.

Downstream consumers also trust the same unpopulated field (would be fine once
strategy publishes it):
- `mlx_startio` (`mlx.c:1766`): `mlxd = bio->bio_driver_info; driveno =
  mlxd->mlxd_drive - sc->mlx_sysdrive;` — wild deref.
- `mlx_completeio` (`mlx.c:1815`): `mlxd = bio->bio_driver_info;` then
  `device_printf(mlxd->mlxd_dev, ...)` — wild deref.
- `mlxd_intr` (`mlx_disk.c:196`): `sc = bio->bio_driver_info;` — wild deref.

The CORRECT source IS set at attach: `mlxd_attach` (`mlx_disk.c:260`) does
`dsk->si_drv1 = sc;` (and `mlxd_open`/`mlxd_close`/`mlxd_ioctl` all correctly
read `dev->si_drv1`). Only `mlxd_strategy` reads the wrong field.

## Reference (correct pattern)
Sibling `amrd_strategy` (`sys/dev/raid/amr/amr_disk.c:167-182`):
```c
cdev_t dev = ap->a_head.a_dev;
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;   // source from si_drv1
if (sc == NULL) { ... EINVAL ... }
bio->bio_driver_info = sc;                                    // publish for downstream
```

## Trigger (theoretical; HW-gated)
Local user with `/dev/mlxd*` access reads/writes; under concurrent vnode I/O
load the recycled pbuf's `bio_driver_info` may hold a non-NULL stale pointer →
wild deref panic in `mlxd_strategy`/`mlx_startio`/`mlx_completeio`.

Not reachable on this guest — no Mylex adapter (`pciconf -l | grep mlx` → no
match), so `mlxd` never attaches and `/dev/mlxd*` does not exist. Runtime
confirmation impossible.

## Fix
Source `sc` from `dev->si_drv1` (set at `mlxd_attach:260`), then publish
`bio->bio_driver_info = sc` for the `mlx_startio`/`mlx_completeio`/`mlxd_intr`
downstream consumers — exactly mirroring `amr_disk.c:172-179`. See `fix.diff`.

The fix matches the finding markdown's `## Recommended fix` proposal (source
from `dev->si_drv1`, publish on `bio_driver_info`).

## Fix validation (Phase 8)
- `fix.diff` applied to `/usr/src` (combined with DF-2073's fix).
- `make -j6 nativekernel KERNCONF=X86_64_GENERIC` → **rc=0, -Werror clean**
  (no errors, no warnings). `kernel.stripped` + `kernel.debug` produced.
- Installed to `/boot/kernel/kernel`; rebooted → `kern.version` bumped
  `#0` → `#1` (Sat Jul 25 11:53:22 UTC 2026); guest boots healthy, no
  regression.
- Runtime before/after NOT possible (HW absent) → `fix_status: not_testable`
  at runtime, but build-validated + source-traced to close the path.

## PoC changes
The README's `mlxd_poc.c` is left as-is (it correctly documents the theoretical
trigger but cannot run without HW). Evidence pack carries the source-trace
citations, the `fix.diff`, the combined build log, and the environment record.
