β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3937

ena_com_admin_destroy() dereferences comp_ctx four lines before its NULL check β€” OOM-gated NULL-deref panic in the ena(4) attach path

Summary

ENA_WAIT_EVENT_DESTROY(admin_queue->comp_ctx->wait_event) dereferences comp_ctx before the NULL guard. When the M_NOWAIT kmalloc in init_comp_ctxt fails, the admin_init error unwind calls the destroy with comp_ctx==NULL -> kernel panic at device attach. Secondary: only comp_ctx[0] of 32 wait events is destroyed. Memory-pressure-gated local DoS. Move-under-the-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3937 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.9 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
fix.diff β€” 634 B view raw
env.txt β€” 984 B view raw
manifest.json β€” 886 B view raw
verdict.json β€” 2.5 KB view raw

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:

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.

VERDICT.md
↓ download raw

DF-3937 β€” VERDICT

Status: inconclusive (hardware-gated) β€” source-level certainty: certain. Impact class: NULL-pointer dereference panic (CWE-476), OOM-gated, at device attach.

Trace

  1. sys/dev/virtual/amazon/ena/ena-com/ena_com.c:1713 β€” ret = ena_com_init_comp_ctxt(admin_queue); inside ena_com_admin_init().
  2. ena_com.c:302-306 β€” queue->comp_ctx = ENA_MEM_ALLOC(queue->q_dmadev, size); (kmalloc(..., M_DEVBUF, M_NOWAIT|M_ZERO), ena_plat.h:297) β€” on failure returns ENA_COM_NO_MEM with comp_ctx == NULL.
  3. ena_com.c:1714-1715 β†’ goto error; β†’ ena_com.c:1761-1762 β€” error: ena_com_admin_destroy(ena_dev);
  4. ena_com.c:1581 β€” ENA_WAIT_EVENT_DESTROY(admin_queue->comp_ctx->wait_event); β†’ ena_plat.h:231-234 (cv_destroy(&w.wq); lockuninit(&w.lock);) β†’ NULL deref panic β€” and this is four lines BEFORE the if (admin_queue->comp_ctx) guard at :1585.
  5. Callers reaching ena_com_admin_init: ena.c:3220 (ena_attach β†’ ena_device_init). So the panic happens in the PCI attach path whenever the 32-entry comp_ctx allocation fails β€” a machine-local memory-pressure window, not attacker-timed in general (an unprivileged local user can spray M_DEVBUF to make NOWAIT allocations fail during a driver_reload/devclass event, which is the realistic DoS shape).

Secondary (same function, non-crashing): only comp_ctx[0]'s wait event is destroyed although all 32 were ENA_WAIT_EVENT_INIT'd (:308-312); the other 31 cv/lock pairs are abandoned β€” on DragonFly cv_destroy/lockuninit are accounting-only, so no resource leak of substance.

Why not reproduced

Requires an ENA PCI function to reach ena attach; the audit guest has none (env.txt, vendor 0x1d0f absent). inconclusive per batch convention. Precedent: DF-3799 (ena.c) documented the same pattern (inverted NULL check) as Info because it was dead code; here the guard order is wrong on a reachable error path, so the finding is filed Low with certainty certain at source level.

Fix

fix.diff moves the ENA_WAIT_EVENT_DESTROY under the existing NULL check.

fix_status

not_testable β€” no ENA hardware on the guest.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Fix not validated on the guest (no ENA hardware, env.txt). fix.diff moves ENA_WAIT_EVENT_DESTROY under the existing NULL guard at ena_com.c:1585 β€” a pure ordering fix authored post-verification.

findings/poc/DF-3937/fix.diff + VERDICT.md
↓ fix.diffper-fix-DF-3937

Confirmed kernel references

Detail

Exploit chain

local memory pressure + ena attach/reload -> ena_com_init_comp_ctxt alloc fails -> ena_com_admin_init error path -> ena_com_admin_destroy -> cv_destroy(&NULL->wq) -> kernel panic (DoS)

Evidence (decisive lines)

findings/poc/DF-3937/VERDICT.md (5-step trace) + fix.diff + env.txt

PoC changes

No seed PoC; hardware-gated.

Verified recommended fix

Move ENA_WAIT_EVENT_DESTROY inside the comp_ctx NULL guard (fix.diff).

Verdict

ena_com_admin_destroy dereferences admin_queue->comp_ctx at ena_com.c:1581 (ENA_WAIT_EVENT_DESTROY -> cv_destroy/lockuninit per ena_plat.h:231-234) FOUR LINES BEFORE the NULL check at :1585. Reached from ena_com_admin_init's error unwind (:1761-1762) when ena_com_init_comp_ctxt's kmalloc (M_NOWAIT) fails (:302-306) β€” i.e. NULL-deref panic in the ena(4) PCI attach path under memory pressure (an unprivileged user can pressure M_DEVBUF around a driver reload to shape this). Secondary: only comp_ctx[0] of the 32 initialized wait events is destroyed. Low severity / certain at source; hardware-gated (no ENA device on guest) => inconclusive runtime classification. Pattern precedent: DF-3799 (dead-code variant, Info).