# DF-4562 — mpssas_add_device re-initializes a target that may still be in
removal / have in-flight commands (speculative list-corruption race)

**File:** `sys/dev/raid/mps/mps_sas_lsi.c:580-682 (sink :653-670),
interaction with :212-219/:328-346/:394-395**
**Severity:** Low (speculative), **Status:** untested — needs an mps HBA
to create the event interleaving; filed as a labeled speculation per pass-2
discipline (lifetimes/error-path interactions), adjacent to the
already-filed DF-3895/DF-3898 removal-path defects.

## The hypothesis

All fw events are processed serialized on `ev_tq` under `mps_lock`
(mps_sas_lsi.c:564-577), but `mpssas_prepare_remove()` only *starts* an
asynchronous removal: it sets `MPSSAS_TARGET_INREMOVAL`, sends a TM
TARGET_RESET, and the removal finishes later in completion callbacks
(mps_sas.c:407-466). If a TARG_ADDED / PD_DELETED event for the same (or a
colliding-ID) device is processed before that removal completes,
`mpssas_add_device()` unconditionally overwrites the target slot
(mps_sas_lsi.c:653-670):

- `targ->flags = 0` clears `MPSSAS_TARGET_INREMOVAL` while the TM is
  still outstanding (`targ->tm` still points at it);
- `TAILQ_INIT(&targ->commands)` / `TAILQ_INIT(&targ->timedout_commands)`
  orphan any still-linked commands — their `tqe_prev` still point into
  the (now reset) head or into each other, so the completion path's
  `TAILQ_REMOVE` writes through stale pointers into the *new* list head
  (list corruption; lost/leaked commands);
- the failure branch at :212-219 itself calls
  `mpssas_prepare_remove()` on a *just-failed* add of the same handle,
  interleaving two removals on one slot.

`mpssas_volume_add()` (:847-853) has the same shape (no INREMOVAL /
non-empty-list check before TAILQ_INIT).

## Why speculative

Triggering requires a specific firmware-event interleaving (removal not
yet completed when the re-add for the same target ID is processed) that
cannot be produced on the QEMU guest (no mps controller). The list
mechanics above are how TAILQ behaves, but the existence of the
interleaving window in the field is unproven; pass-1 findings DF-3895 /
DF-3898 cover neighboring removal-lifetime defects, so this is recorded
as the remaining uncovered edge rather than a demonstrated primitive.

## Suggested hardening (defensive; also collapses this window)

In `mpssas_add_device` / `mpssas_volume_add`, before overwriting the slot:
bail out (EBUSY) if `targ->handle != 0 && (targ->flags &
MPSSAS_TARGET_INREMOVAL || !TAILQ_EMPTY(&targ->commands) ||
!TAILQ_EMPTY(&targ->timedout_commands) || targ->tm != NULL)` — see
fix.diff sketch.
