DF-3519 / verdict.json
{ "finding_id": "DF-3519", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "NOT REPRODUCED (vector-blocked), code-certain: ng_pptpgre_start_recv_ack_timer() and ng_pptpgre_start_send_ack_timer() do plain non-atomic node->refs++ (ng_pptpgre.c:768, :871) racing ng_unref()s plain --node->refs (ng_base.c:474-485, crit-enter protects the current CPU only) and plain priv->timers++/-- across contexts. A lost RMW update undercounts refs, freeing struct ng_node while the armed timers callback still dereferences it (recv/send_ack_timeout :802/:906) and while ng_unrefs LIST_REMOVE corrupts the global node hash lists - UAF on the node object. DF-0492s refcount also racy instantiated for ng_pptpgre; not covered by DF-0596 (xmitWin/timeSent scope). Instruction-level window requires concurrent timer start/stop on multiple CPUs, unreachable from userspace ng_socket (CPU-0 netisr serialization, DF-0590 empirical).", "exploit_chain": "", "evidence": "VERDICT.md code trace (ng_pptpgre.c:768-769/:871-872 vs ng_base.c:474-485, callbacks :811/:915/:802/:906); no runnable vector on this guest - documented rather than burning a run that can only reproduce the known serialization negative", "kernel_refs": [ "sys/netgraph/pptpgre/ng_pptpgre.c:768", "sys/netgraph/pptpgre/ng_pptpgre.c:769", "sys/netgraph/pptpgre/ng_pptpgre.c:871", "sys/netgraph/pptpgre/ng_pptpgre.c:872", "sys/netgraph/netgraph/ng_base.c:474", "sys/netgraph/pptpgre/ng_pptpgre.c:802" ], "poc_changes": "No binary PoC authored: the only userspace vector (ng_socket) is serialized on CPU 0 by construction; DF-3518s 52k-lifecycle hammer already re-confirmed the blocker on this node type this run.", "attempts": 0, "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": 0, "guest_dirty": 0, "build_cmd": "", "run_cmd": "", "code_hash": "", "notes": "Double-free direction analyzed and killed: priv cannot be double-freed via timers accounting because ng_pptpgre_rmnodes reset uses synchronous callout_stop before checking timers==0 (callbacks cannot still be in flight). Only the undercount direction (premature node free) survives analysis.", "recommended_fix": "Use NG_NODE_REF(node) (atomic_add_int, netgraph.h:132) at both start sites and atomic_add_int for priv->timers; better, add the per-node serialization lock (also fixes DF-0590/DF-3518 class).", "fix_status": "not_testable" } |