# DF-4698 — nvme(4): queue-creation retry path re-allocates live subqueues/comqueues → double `lockinit` + permanent DMA/request-pool leak

## Where
`sys/dev/disk/nvme/nvme_admin.c` `nvme_admin_state_make_queues()` — error unwind asymmetry
between the completion-queue and submission-queue loops (lines 453–515) interacting with
`nvme_alloc_subqueue`/`nvme_free_subqueue` (`sys/dev/disk/nvme/nvme.c:126–299`).

## Trace
1. comq creation fails at index k → the comq unwind loop (nvme_admin.c:498-501) frees
   comqueues k..1 (correct, `comq_err_idx` includes the failed one via the `++i` at :465).
2. The **subq loop still runs** (nvme_admin.c:471-485): `nvme_alloc_subqueue(sc,1)` succeeds
   (pure memory op), `nvme_create_subqueue(sc,1)` fails because its completion queue no
   longer exists → `break` at :482 with `i = 1` and **no** `++i` → `subq_err_idx = 1` →
   the unwind loop `for (i = subq_err_idx - 1; i >= 1; --i)` (nvme_admin.c:494-497) runs
   **zero iterations** → subqueues[1] stays allocated (reqary kmalloc + sque/prps/adm DMA
   maps) but uncreated.
3. `goto tryagain` (nvme_admin.c:514) with 1/1 queues → `nvme_alloc_subqueue(sc,1)` runs
   again on the still-allocated queue:
   * `nvme.c:135` `lockinit(&queue->lk, …)` on an already-initialized lock;
   * `nvme.c:145/163` `bus_dmamem_alloc` overwrites `queue->ksubq`/`queue->kprps` and
     `nvme.c:197` overwrites `queue->reqary` **without freeing the previous allocations**
     → one full set of queue DMA buffers + request array leaks permanently (the old
     pointers are gone; teardown frees only the current ones).
   The identical asymmetry exists for the comq loop if `nvme_alloc_comqueue` itself fails
   after a prior successful retry-less attempt.

## Threat
Requires a controller that fails an I/O-queue creation and then succeeds on the 1/1 retry
("device lied to us" path, nvme_admin.c:503-514) — firmware malfunction or a malicious
device. Impact: permanent kernel/DMA memory leak (repeatable on every controller reset
cycle) and re-initialization of a live lock. No direct memory-safety violation on the
leaked objects (they are simply lost).

## PoC
Not runnable on the audit guest: stock QEMU 7.2 always succeeds queue creation, and no
mechanism exists to make it fail selectively. `untested` (analysis-certain).

## Fix sketch
* unwind the subq loop with the same `++i`-inclusion convention as the comq loop, and
* before re-allocating a queue in the `tryagain` path, `nvme_free_subqueue()`/`nvme_free_comqueue()`
  any still-allocated queue in 1..N (or track allocation state and refuse double alloc).
