# DF-4211 — mpt_refresh_raid_vol frees an in-flight RAID request on ETIMEDOUT

- **File**: `sys/dev/disk/mpt/mpt_raid.c`
- **Lines**: 1327–1334
- **Class**: use-after-free / request-pool corruption (CWE-415/CWE-672)
- **Bucket**: memcorrupt
- **Hardware gate**: LSI MPT Fusion RAID controller (mpt(4) RAID personality)

## What happens

`mpt_refresh_raid_vol()` issues `MPI_RAID_ACTION_INDICATOR_STRUCT` synchronously
(`mpt_issue_raid_req(..., /*wait*/TRUE)` → `mpt_wait_req(..., sleep_ok=FALSE,
time_ms=2000)`, `sys/dev/disk/mpt/mpt_raid.c:1327-1328`,
`sys/dev/disk/mpt/mpt.c:1316-1363`). On timeout the request is **still**

1. queued to the IOC (`REQ_STATE_QUEUED`, frame owned by the chip), and
2. linked on `mpt->request_pending_list` (inserted by `mpt_send_cmd`,
   `mpt.c:1298`; nothing removes it on the timeout path).

yet the code immediately does `mpt_free_request(mpt, req)` (`mpt_raid.c:1332`).

Every sibling helper in the same file handles this correctly by *leaving* the
request for the late-completion path (`mpt_verify_mwce` 977–981,
`mpt_verify_resync_rate` 1030–1034 and 1066–1070 all `return` without
freeing; the late reply is freed by `mpt_raid_reply_handler` via
`free_req=TRUE`, `mpt_raid.c:543-547`, exactly like
`mpt_config_reply_handler`'s documented "late completion" path,
`mpt.c:520-525`).

## Corruption chain (production kernel, no INVARIANTS)

1. `mpt_free_request` inserts the request on `request_free_list` while it is
   still on `request_pending_list` (the `KASSERT(mpt_req_on_pending_list
   == 0)` guard, `mpt.c:1213`, only exists in INVARIANTS builds —
   `sys/sys/systm.h:95-119`).
2. `mpt_get_request` hands the same request to a new owner (e.g. a CAM
   SCSI I/O from `mpt_cam.c`); `mpt_send_cmd` inserts it into
   `request_pending_list` a second time → the TAILQ now contains the same
   node twice.
3. The IOC eventually completes the original RAID action (slow controller
   during resync — the exact condition that produced the timeout). The
   reply's `MsgContext` is `req->index | raid_handler_id`
   (`mpt_raid.c:616`), so `mpt_intr` dispatches
   `mpt_raid_reply_handler` on the request that now belongs to the new
   owner: it `TAILQ_REMOVE`s it, marks it DONE and frees it → the new
   owner completes/frees a request it no longer owns → double-free /
   cross-command use-after-free with attacker-influenced (frame) contents.

Trigger is firmware/hardware timing (IOC > 2 s to answer a RAID action while
a volume resync is in progress — this code path only runs when
`MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS` is set). Same trust boundary as
the known fw-fed findings DF-1449/DF-2484/DF-2485 (excluded here — this is a
different bug class: request lifecycle, not index validation).

## Verification status

**untested — hardware-gated.** The QEMU audit guest has no LSI MPT Fusion
HBA (`pciconf -l` shows only virtio/bochs devices, see `env.txt`); `mpt.ko`
never attaches, so the RAID personality (and this path) cannot execute.
Source-level certainty: the timeout/free/late-reply protocol is fully
traceable in-tree (see VERDICT.md). No uid=0 chain is developed because the
primitive cannot be exercised without the controller.

## Reproduce (on hardware)

1. System with mpt(4) RAID volume, start a resync
   (`mpt_raid.c:1317` requires RESYNC_IN_PROGRESS for the indicator fetch).
2. Saturate/wedge the IOC so the `MPI_RAID_ACTION_INDICATOR_STRUCT` reply
   takes > 2 s (or drop it and let it complete late).
3. Observe `mpt_refresh_raid_vol: Progress Indicator fetch timeout` on
   console, then either the INVARIANTS panic
   (`mpt_free_request: req %p:%u func %x on pending list`) or, on a
   production kernel, later list-corruption panics from
   `request_pending_list`/`request_free_list` double-linking.

## Fix

`fix.diff` — drop the `mpt_free_request()` on the ETIMEDOUT path so the
request is reclaimed by the late reply, matching every sibling helper.
