⬢ DragonFlyBSD Kernel Audit
DF-4754 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4754",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced on the DragonFly 6.5-DEVELOPMENT QEMU guest: a completion descriptor with req_id=0xFFFF (hostile host DMA payload) passes through ena_com_rx_pkt unvalidated (rc=0) while the TX path rejects the same value; replicating the drivers consumer line (ena.c:1459) indexes rx_buffer_info[0xFFFF] 4031 KB past a 1024-entry ring, and the drivers next dereference (ena.c:1462, rx_info->mbuf) page-faults the kernel (Fatal trap 12, guest dead in DDB). With mapped memory at the OOB offset the same primitive yields a foreign-memory-derived mbuf* that the driver writes through and delivers to if_input (kernel-memory disclosure + semi-controlled wild-pointer writes). Attacker is the compromised host (paravirt device), so this is guest hardening, not guest->guest escalation. fix.diff (mirror of the TX-side q_depth check) validated on the same real source file: patched build returns EINVAL, propagates nothing, no panic, guest stays up.",
  "exploit_chain": "compromised host DMAs rx cdesc {PHASE|LAST, len, req_id=0xFFFF} -> ena_com_rx_pkt copies req_id into ena_bufs[0] with no check (ena_eth_com.c:540) -> ena_rx_mbuf: rx_info = &rx_buffer_info[0xFFFF] (ena.c:1459, ~4MB past 64KB array) -> mbuf = rx_info->mbuf (OOB read; unmapped => kernel panic as demonstrated; mapped => wild struct mbuf*) -> driver writes m_pkthdr.len/m_len (device-chosen), m_flags, rcvif through the wild pointer, unloads an OOB dmamap, and passes the mbuf to if_input => kernel memory disclosure to the network stack + constrained arbitrary writes. Demonstrated through the OOB read (panic); wild-pointer write/leak stages deliberately not driven (host already owns guest memory; hardening is the deliverable).",
  "evidence": "run.log/panic.txt lines: ena_com_rx_pkt rc=0 descs=1, ena_bufs[0].req_id=0xffff *** UNVALIDATED req_id PROPAGATED TO DRIVER ***, rx_buffer_info[0xffff] -> 0xfffff8011c0dffc0 (4031 KB past ring end), Fatal trap 12: page fault ... fault virtual address = 0xfffff8011c0dffc0, Stopped at df4754_run+0x2e1. Contrast: tx_comp_req_id_get(req_id=0xffff) rc=22 -> REJECTED. Fix: fix_run.log ena_com_rx_pkt rc=22 descs=0, req_id=0x0000 rejected, no panic, guest up.",
  "kernel_refs": [
    "sys/dev/virtual/amazon/ena/ena-com/ena_eth_com.c:536",
    "sys/dev/virtual/amazon/ena/ena-com/ena_eth_com.c:614",
    "sys/dev/virtual/amazon/ena/ena.c:1459",
    "sys/dev/virtual/amazon/ena/ena.c:1491",
    "sys/dev/virtual/amazon/ena/ena.c:736"
  ],
  "poc_changes": "No seed PoC existed (pass-2 finding). Harness written from scratch: kld module compiling the guests real /usr/src ena_eth_com.c; had to (a) include ena_eth_com.h (not just ena_com.h) for the prototypes, (b) add device_if.h/bus_if.h/pci_if.h to SRCS, (c) build with make -m /usr/share/mk -m /usr/src/sys/conf SYSDIR=/usr/src/sys, (d) define the ena_log_level extern, (e) mirror struct ena_rx_buffers 64-byte __cachealign layout.",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "runtime_sec": 95,
  "guest_dirty": 0,
  "build_cmd": "cd /root/df4754 && make -m /usr/share/mk -m /usr/src/sys/conf SYSDIR=/usr/src/sys",
  "run_cmd": "cd /root/df4754 && kldload ./df_rxreqid.ko",
  "code_hash": "2a89239e9dede77126cde2a99f99714b1949dc511511948442a87d05e1f02ed7",
  "notes": "ENA hardware absent on the audit guest by construction; the hostile completion ring is synthesized in kernel memory exactly as the device DMA would present it, and the code under test is the real ena_eth_com.c compiled from the guest /usr/src. Guest reset to clean-source snapshot after both runs.",
  "recommended_fix": "Add if (unlikely(cdesc->req_id >= io_cq->q_depth)) return ENA_COM_INVAL; to the copy loop of ena_com_rx_pkt (mirror of the TX check at ena_eth_com.c:614), plus defense-in-depth validate_rx_req_id() use in ena_rx_mbuf (ena.c:1458/1490).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to the guest /usr/src copy of ena_eth_com.c; module rebuilt from the patched file and the exact PoC re-run: baseline panic (Fatal trap 12 at the OOB rx_info address) is gone, ena_com_rx_pkt now returns EINVAL and propagates no req_id, guest stays up. Validated at module level against the real source file; in-driver validation would additionally require ENA hardware.",
  "fix_evidence": "fix_build.log (clean rebuild of patched ena_eth_com.c); fix_run.log (ena_com_rx_pkt rc=22 descs=0, req_id=0x0000 rejected, no trap, vm stays up) vs baseline panic.txt (Fatal trap 12, fault va 0xfffff8011c0dffc0)."
}