DF-3482 / verdict.json
{ "finding_id": "DF-3482", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "ng_ppp_mp_strategy (ng_ppp.c:1746-1749) sets the file-scope global compareLatencies to the callers STACK array, kqsort()s with ng_ppp_intcmp dereferencing it (:1840), then stores NULL -- with no lock. netgraph7 funnels all items to cpu0, which is why DF-0330 was classified latent; v1 (the LIVE stack) has no such funnel for transmit: the ng_iface output path runs ng_ppp_output -> ng_ppp_mp_output -> ng_ppp_mp_strategy in the sending processs thread (per-interface serializer only, no BGL, no netisr), so two bundles driven from different CPUs (or one bundle driven by two senders) execute strategy concurrently and thread As trailing compareLatencies = NULL turns thread Bs in-flight comparator call into a NULL+index read => kernel panic (cross-bundle interleavings additionally sort against a dead stack array). The defect is certain by inspection, but on this stock guest it could not be staged: ng_iface and ng_eiface node creation panics the kernel deterministically (panic: trying to free NULL pointer, ng_iface_constructor+0x3a2 / ng_eiface_constructor+0xf6, from a plain root ngctl mkpeer . iface i0 inet on a fresh boot -- logs in this pack), and the ng_ether upper-hook alternative severs the guests ssh. Severity Medium (panic/DoS on MP-capable bundles with non-round-robin, unequal links); trigger in a real deployment is any concurrent routed/local traffic through two iface-fed MP bundles.", "exploit_chain": "", "evidence": [ "findings/poc/DF-3482/panic.iface_constructor.txt \u2014 deterministic blocker: iface node creation panics stock kernel", "findings/poc/DF-3482/panic.eiface_constructor.txt \u2014 eiface variant of the same creation panic", "findings/poc/DF-3482/pppstrat.c \u2014 intended two-bundle iface-fed strategy race graph builder", "sys/netgraph/ppp/ng_ppp.c:1746-1749 \u2014 global set/kqsort/NULL-clear", "sys/netgraph/ppp/ng_ppp.c:1840 \u2014 comparator deref of the global", "sys/netgraph/iface/ng_iface.c ng_iface_output \u2014 process-thread unfuneled context" ], "kernel_refs": [ "sys/netgraph/ppp/ng_ppp.c:363", "sys/netgraph/ppp/ng_ppp.c:1746", "sys/netgraph/ppp/ng_ppp.c:1840", "sys/netgraph/iface/ng_iface.c:408" ], "poc_changes": "Harness complete (pppstrat.c builds two 6-unequal-link bundles fed by ng_iface interfaces with routes); never executed past graph construction because mkpeer of the iface node itself panics the stock kernel.", "attempts": 4, "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": 1500, "guest_dirty": 0, "build_cmd": "cc -O2 -w -o pppstrat pppstrat.c", "run_cmd": "kldload ng_socket ng_ppp ng_ksocket ng_iface; ./pppstrat 2; ping -f 10.240.0.9 & ping -f 10.241.0.9 &", "code_hash": "1f598b66e6525e4b98ed1a1cac248ce2e2647e57c129b3aa1f8e51ad50a546e2", "notes": "The ng_iface/ng_eiface creation panic is itself a deterministic root-reachable crash on the stock kernel and deserves its own finding for sys/netgraph/iface/ng_iface.c + sys/netgraph/eiface/ng_eiface.c (both hand-kmalloc an ifnet and if_attach it; something in that shared path frees NULL on this kernel). Guest was reset with-src afterwards.", "recommended_fix": "Replace the global+kqsort with a local insertion sort over the <=NG_PPP_MAX_LINKS entries using only stack state (see fix.diff), or pass the latency array via a qsort thunk if one is added.", "fix_status": "not_testable", "fix_kernel_uname": "", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored (local insertion sort); not kernel-validated because the baseline race could not be staged on this guest (iface creation panic).", "fix_evidence": "findings/poc/DF-3482/fix.diff" } |