# DF-5594 — devq lifetime is anchored to the sim refcount, not to the peripherals queued on it

`sys/bus/cam/cam_sim.c` pass-2 — `cam_sim_release()` / `cam_simq_release()` interplay.

## What

The comment above `cam_sim_release()` (cam_sim.c:236-240) states the design
intent: *"the devq is still used by individual peripherals even if the backend
sim disappears, so do not destroy it until the sim itself is no longer
needed."* The code does the opposite of the intent:

* `cam_devq`'s reference count counts **only sims** (`cam_devq_reference` is
  called solely from `cam_sim_alloc`, cam_sim.c:185) plus the initial 1 from
  `cam_devq_alloc`. Devices (`struct cam_ed`) are inserted into
  `devq->alloc_queue`/`send_queue` (`xpt_schedule_dev_allocq` /
  `xpt_schedule_dev_sendq`) **without taking any devq reference**.
* When the last sim reference dies, `cam_sim_release()` (cam_sim.c:247-250)
  calls `cam_simq_release(sim->devq)` → `cam_devq_release()`
  (cam_queue.c:247-257) → refcount 0 → `kfree(devq)` — **while `cam_ed`
  allocation slots may still be queued** in its heap arrays. The release-side
  warning checks only `alloc_active`/`send_active` counters, never
  `alloc_queue.entries`/`send_queue.entries`.
* `xpt_bus_deregister()` tries to drain the devq queues but **gives up after
  5 retries and proceeds anyway** (cam_xpt.c:4439-4446), leaving queued
  `cam_ed_qinfo` entries behind; the subsequent detach then frees the devq
  under them.
* Downstream: the queued devices' `alloc_ccb_entry.pinfo.index` /
  `send_ccb_entry.pinfo.index` remain set (pointing into the freed arrays);
  when the device is finally released, `xpt_release_device()` hits its guard
  and **panics**: `panic("Removing device while still queued for ccbs")`
  (cam_xpt.c:5199-5205). Between devq-free and device-release the queue
  arrays are freed memory containing pointers to live `cam_ed` objects —
  latent UAF surface, currently fenced off only by the `bus->sim ==
  &cam_dead_sim` retarget (dead_sim.devq == NULL gates all dispatch).

Corroboration captured live during the DF-5592 rig's detach flow (serial
console): `cam_devq_release: WARNING active allocations 8 active send 8!` —
the devq dropped with active I/O after a warning, exactly the DF-5552 /
DF-5594 provenance, reached through a real deregistration sequence.

## Threat

Storage detach/deregistration with a device stuck queued for ccb allocation
(the DF-5550 scenario: periph dying of AC_LOST_DEVICE while scheduled; frozen
queues) → freed devq with live queue entries → controlled
`panic("Removing device while still queued for ccbs")` (local DoS at minimum),
plus a heap-UAF window that today has no in-tree walker but no structural
guarantee either.

## Status

Code-trace verified (path:line above); not dynamically PoC'd — building the
stuck-device precondition requires the DF-5550 corruption state or a frozen
alloc queue at detach. Evidence class: trace + the captured warning line.
