# DF-3937 — ena_com_admin_destroy() dereferences comp_ctx BEFORE its NULL check (OOM-gated NULL deref at attach)

## Finding
`sys/dev/virtual/amazon/ena/ena-com/ena_com.c:1581`:

```c
void ena_com_admin_destroy(struct ena_com_dev *ena_dev)
{
	...
	ENA_WAIT_EVENT_DESTROY(admin_queue->comp_ctx->wait_event);   /* :1581 — deref */

	ENA_SPINLOCK_DESTROY(admin_queue->q_lock);

	if (admin_queue->comp_ctx)                                   /* :1585 — NULL check */
		ENA_MEM_FREE(ena_dev->dmadev, admin_queue->comp_ctx);
```

`ENA_WAIT_EVENT_DESTROY(w)` expands (DragonFly port, `ena_plat.h:231-234`) to
`cv_destroy(&((w).wq)); lockuninit(&((w).lock));` — i.e. it dereferences the
pointer. If `ena_com_admin_init()` failed in `ena_com_init_comp_ctxt()`
(`ena_com.c:1713-1715` → `ena_com.c:302-306`: `comp_ctx = ENA_MEM_ALLOC(...)`
returned NULL → `return ENA_COM_NO_MEM`), the error path
`ena_com.c:1761-1762` calls `ena_com_admin_destroy(ena_dev)` with
`admin_queue->comp_ctx == NULL` → NULL dereference panic during device
attach. Additionally, even on the success path only `comp_ctx[0]`'s wait
event is destroyed although all `q_depth` (32) entries were initialized in
`ena_com_init_comp_ctxt` (`ena_com.c:308-312`) — benign on DragonFly
(cv_destroy/lockuninit are statistical no-ops) but sloppy lifetime handling.

## Trigger
ENA device attach under kmalloc failure for the 32-entry `comp_ctx` array
(`M_DEVBUF, M_NOWAIT`). The DF-3799-analogue pattern (inverted/absent NULL
guard) but *reachable* on an allocation failure, not dead code.

## Severity rationale
Low: requires local memory pressure at attach time on a machine with an ENA
NIC; impact is a kernel NULL-deref panic (DoS), no corruption (write is via
NULL+offset within the cv/lock structs at address ~0 — fault).

## Why inconclusive on this guest
No ENA PCI function on the audit guest (env.txt); the destroy path only runs
from ena(4) attach/detach.
