⬢ DragonFlyBSD Kernel Audit
DF-4219 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4219",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Source-certain, runtime hardware-gated (inconclusive per family convention). (1) ae_rx_intr() (if_ae.c:643-668) has NO work bound: the `for(;;)` walk terminates only on a descriptor without the controller-DMAd AE_RXD_UPDATE bit (:647); the driver clears the bit (:649) and advances rxd_cur % 64 (:657) but nothing stops the device from re-marking, and no budget exists anywhere in if_ae.c. A faulty/malicious controller keeping the bit set spins ae_intr() forever holding ifp->if_serializer (:754): each iteration m_devget+if_input (:616/:625), the AE_MB_RXD_IDX_REG mailbox write (:671) never happens \u2014 CPU + softint livelock until reboot (DoS). NOT remotely triggerable by packets alone (device-paced, same bar as DF-3990 ale). Walk model built and run on the guest (run.log RC=0): honest device 5 iterations/normal exit; keeps-marking device 1,000,000 capped iterations in ONE call, ring wrapped 15,625x, budget consulted 0. Guest has no 1969:2048 device, if_ae not in X86_64_GENERIC, guest src md5 == audited tree. DoS only.",
  "exploit_chain": "On real hardware: faulty or malicious controller (bad silicon, errant DMA, hostile PCIe passthrough NIC \u2014 DF-1452s device model) keeps AE_RXD_UPDATE ahead of the drivers clear -> ae_intr never returns from ae_rx_intr, serializer never released, one CPU consumed and mbuf pressure from per-iteration m_devget -> ae0 + anything needing ifp->if_serializer wedged until reboot. No privilege path.",
  "evidence": "run.log (honest vs keeps-marking walk, RC_4219=0, budget consulted 0), trigger.c (walk model), env.txt (NO_ATTANSIC_PCI_1969 / no if_ae in kernel config / guest-src md5 match), VERDICT.md (path:line trace), fix.diff (per-interrupt budget, git-apply clean)",
  "kernel_refs": [
    "sys/dev/netif/ae/if_ae.c:643",
    "sys/dev/netif/ae/if_ae.c:647",
    "sys/dev/netif/ae/if_ae.c:649",
    "sys/dev/netif/ae/if_ae.c:657",
    "sys/dev/netif/ae/if_ae.c:671",
    "sys/dev/netif/ae/if_ae.c:754",
    "sys/dev/netif/ae/if_aereg.h:384"
  ],
  "poc_changes": "No seed PoC. Authored trigger.c walk model; first guest compile warned on a model-local unused-but-set variable, removed and rebuilt clean before the decisive run (run.log is from the clean build).",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 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": 150,
  "guest_dirty": 0,
  "build_cmd": "vm.sh run_root cc -O2 -Wall -Wextra -o /tmp/df4219 /tmp/trigger.c (after scp -F dfbsd-qemu/config trigger.c dfbsd:/tmp/)",
  "run_cmd": "vm.sh run_root /tmp/df4219; echo RC_4219=$?",
  "code_hash": "f2158bf97ee34f07b36d43930d88e0cae7bc4229e1d6fca90fdc9d72b2aa7ca5",
  "notes": "Pass-2 result for if_ae.c (GLM 5.3). DF-3761 (alc) / DF-3778 (jme) / DF-3934 (age) / DF-3990 (ale) family instance, ae(4). Budget fix mirrors what the ale fix (DF-3990) does: leave remaining descriptors for the next interrupt; RX re-arm relies on the still-enabled IMR + interrupt moderation timer.",
  "recommended_fix": "Add a per-interrupt frame budget (AE_RXD_COUNT_DEFAULT) to ae_rx_intrs for(;;) walk so a device that keeps AE_RXD_UPDATE set cannot spin the handler forever (see fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Baseline walk-unboundedness proven by the model; the ae-specific fix path cannot be runtime-validated (no Attansic L2 hardware on the guest). fix.diff adds the same per-interrupt budget shape validated for the DF-3990 ale family (leave the rest for the next interrupt; IMR stays enabled); git-apply clean against the audited tree.",
  "fix_evidence": "fix.diff (git-apply --check clean); run.log [1] vs [2] contrast"
}