DF-4068 / verdict.json
{ "finding_id": "DF-4068", "status": "inconclusive", "reproduced": 0, "impact": "none observed (hardware-gated); source-level: hard CPU livelock with host interrupts masked and ifnet serializer held (permanent DoS of the system) from a single out-of-range DMAd 32-bit producer/consumer word; txeof variant also underflows sc->ti_txcnt per spin", "confidence": "certain", "verdict": "Source-level certain, runtime hardware-gated (inconclusive per family convention). All three ti_intr() completion walks terminate on raw equality between a driver-side consumer index (uint16_t, kept in [0,ring_cnt) by TI_INC modulo, if_tireg.h:965) and a 32-bit word the Tigon firmware DMA-writes into host memory (struct ti_producer.ti_idx, if_tireg.h:485-488): ti_rxeof if_ti.c:1629 (ring 2048), ti_txeof if_ti.c:1729 (ring 512), ti_handle_events if_ti.c:511 (ring 256). A DMAd value outside the consumers modular range (high bit set, or >= ring size as a raw integer: 4096, 0x8000, 0xFFFFFFFF) makes equality unsatisfiable, so the loop never exits. It spins inside the interrupt handler AFTER host interrupts were masked (TI_MB_HOSTINTR=1, if_ti.c:1778) while holding ifp->if_serializer (intr registered with the ifnet serializer, if_ti.c:1567-1569): the CPU never returns from the IRQ -- hard livelock; txeof additionally decrements sc->ti_txcnt (int, if_ti.c:1755) each spin. No work bound exists anywhere in the three loops (DF-3761 alc unbounded-walk class, stronger form: the termination condition itself is unsatisfiable, one poisoned word suffices, no flood needed). Runtime: audit guest has zero Tigon-family PCI devices (env.txt), so the poisoned word cannot be instantiated; trigger.c simulates benign (terminates in 37/511/3 iterations) and poisoned (never terminates in 8x ring budget) walks on the guest (run.log RC=0).", "exploit_chain": "On real (faulty/malicious) Tigon/Tigon2 hardware or PCIe DMA tampering: firmware/DMA writes one out-of-range 32-bit value into ti_return_prodidx_r (or ti_tx_considx_r / ti_ev_prodidx_r) -> next interrupt enters ti_rxeof/ti_txeof/ti_handle_events, masks host interrupts, spins forever holding the serializer -> permanent system livelock (all cores touching the serializer or the IRQ line starve; watchdog cannot preempt a spinning hard IRQ context).", "evidence": "run.log (benign walks terminate; poisoned 0x8000/0x1000/0x10000/0xffffffff walks NEVER TERMINATE in budget, RC=0); build.log; env.txt (no Tigon PCI devices on guest); VERDICT.md (path:line trace + cleared walk surfaces); trigger.c; fix.diff; fix_build.log (patched module compiles -Werror clean on guest)", "kernel_refs": [ "sys/dev/netif/ti/if_ti.c:511", "sys/dev/netif/ti/if_ti.c:1629", "sys/dev/netif/ti/if_ti.c:1729", "sys/dev/netif/ti/if_ti.c:1755", "sys/dev/netif/ti/if_ti.c:1778", "sys/dev/netif/ti/if_ti.c:1567", "sys/dev/netif/ti/if_ti.c:1248", "sys/dev/netif/ti/if_ti.c:1323", "sys/dev/netif/ti/if_ti.c:1357", "sys/dev/netif/ti/if_tireg.h:485", "sys/dev/netif/ti/if_tireg.h:965", "sys/dev/netif/ti/if_tireg.h:1141" ], "poc_changes": "No seed PoC (no prior findings on this file). Authored trigger.c (bounded-budget termination simulator) per the DF-4024 convention; honest hardware-absent verification.", "attempts": 3, "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": 35, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -Wextra -o /tmp/df4068 /tmp/df4068.c", "run_cmd": "vm.sh run_root /tmp/df4068; echo RC=$?", "code_hash": "dff0e3ea421982e94fabb04411b47ac35472a576292a76cc194218a846e823f3", "notes": "Pass-2 checklist item (b)/(c) for if_ti.c. Note there is no ti_tick callout in this driver (periodic work rides the NICs own stats-update events), so the DF-3715 callout AB-BA shape does not apply; watchdog (if_timer) runs under the ifnet serializer on DFly and is serialized against ti_intr. Cleared: event-ring array access masked; ti_start prodidx is host-register not DMA; encap bounded; Tigon-1 banked TX window in-bounds.", "recommended_fix": "Give each completion walk a ring-sized iteration budget (for budget = TI_..._RING_CNT; cond && budget > 0; budget--) so a poisoned DMAd index costs one rings work per interrupt instead of the CPU, see fix.diff.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff adds ring-sized work budgets to all three walks (standard bound for the DF-3761 class); git apply --check passes and the patched if_ti.ko builds clean on the guest with -Werror -std=gnu11 (fix_build.log FIXBUILD_f3_RC=0); runtime validation impossible without Tigon hardware.", "fix_evidence": "findings/poc/DF-4068/fix.diff + findings/poc/DF-4068/fix_build.log" } |