# DF-4818 — nvme(4): unchecked MSI-X vector IRQ resource allocation + missing
MSI-X teardown on fallback (nvme_pci_attach)

## What this is

`nvme_pci_attach()` (sys/dev/disk/nvme/nvme_attach.c:149-205) requests one
`SYS_RES_IRQ` per MSI-X vector:

```c
160:  for (i = 0; error == 0 && i < sc->nirqs; ++i) {
162:      error = pci_alloc_msix_vector(dev, i, &sc->rid_irq[i], cpu);
164:      if (error)
165:          break;
166:      sc->irq[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
167:                                          &sc->rid_irq[i], RF_ACTIVE);
```

The return of `bus_alloc_resource_any()` is **never checked**, and after the
loop only `sc->irq[0]` is validated (line 239). Three consequences, all
boot/kldload-time kernel panics:

1. **NULL irq reaches bus_setup_intr** — if `bus_alloc_resource_any` fails for
   vector k>=1 (kernel memory pressure in `rman_reserve_resource`, or rman/map
   desync) while vector 0 succeeds, attach completes with `sc->irq[k] == NULL`.
   The admin thread later runs `nvme_create_comqueue()` which calls
   `bus_setup_intr(sc->dev, sc->irq[ivect], ...)` (nvme.c:823) for
   `ivect == qid`; on NULL the call chain reaches
   `nexus_setup_intr()` which does an unconditional
   `panic("nexus_setup_intr: NULL irq resource!")`
   (sys/platform/pc64/x86_64/nexus.c:504-506).

2. **NULL irq hits the error unwind** — if instead a LATER vector fails in
   `pci_alloc_msix_vector` after an earlier vector's resource alloc returned
   NULL, the unwind loop (nvme_attach.c:192-199) calls
   `bus_release_resource(dev, SYS_RES_IRQ, rid, NULL)` →
   `resource_list_release()` → unconditional
   `panic("resource_list_release: resource entry is not busy")`
   (sys/kern/subr_bus.c:2738-2739).

3. **Missing pci_teardown_msix on the MSI-X→legacy fallback** — when
   `pci_setup_msix()` succeeded but a vector allocation then fails, the
   fallback path (line 225) overwrites `sc->irq_type`, and detach
   (line 508-517) only tears MSI-X down for `irq_type == PCI_INTR_TYPE_MSIX`.
   `msix_table_res`/`msix_pba_res` stay set, so a later re-attach of the same
   device hits `KASSERT(... "MSI-X has been setup yet")` in `pci_setup_msix()`
   (sys/bus/pci/pci.c:1644-1645). INVARIANTS kernels only — which includes
   the GENERIC config of this audit (sys/config/X86_64_GENERIC:56), as proven
   live by the unrelated vtpci KASSERT panic captured in run.3.log.

## How to build / run

There is no C PoC: the trigger is an environmental failure inside the kernel
resource allocator at driver-attach time. The evidence pack instead
live-validates every *reachable* sub-path of the MSI-X error machinery on the
QEMU guest, bracketing the defect to exactly the unchecked NULL:

1. `./run.sh baseline` — boot `dfbsd-qemu/vm_nvme.sh`
   (1 nvme controller, msix_qsize=65, -smp 6): expect
   `nvme0: mapped 7 MSIX IRQs` (= min(65, ncpus+1)).
2. `./run.sh emptypool` — add `hw.ioapic.msi_start="192"` to /boot/loader.conf
   and reboot: per-cpu MSI pool empty → every `pci_alloc_msix_vector` fails at
   i=0 → unwind no-op → legacy fallback: `nvme0: mapped 1 normal-int IRQs`,
   disk still attaches. (run.2.log)
3. `./run.sh midloop` — `dfbsd-qemu/vm_nvme24.sh` (24 controllers, -smp 2)
   + `hw.ioapic.msi_start="160"` (pool 2x32=64 slots vs 24*3=72 demanded):
   nvme21 fails MID-loop, the unwind loop runs with a live allocated vector,
   releases it, falls back; nvme21..23 attach their disks; **no nvme panic**.
   (run.3.log)

The panic itself (manifestations 1-3) could not be forced on the emulator:
manifestation 1/2 need `rman_reserve_resource` to fail on a freshly granted
MSI-X irq number (kernel-memory pressure / desync — the per-cpu map and rman
are derived from the same source, sys/platform/pc64/apic/ioapic_abi.c:1200-
1245), and manifestation 3 needs a re-attach cycle of a driver compiled into
the kernel (no sys/modules/nvme exists).

## Expected output

See run.log (baseline), run.2.log (empty pool), run.3.log (mid-loop + the
incidental vtpci panic — a *different* driver's bug, see VERDICT.md).
