DF-4219 / verdict.json
{ "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" } |