{
  "finding_id": "DF-3481",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "v1 ng_ppp (LIVE stack, LEGACY bucket) guards NONE of its private state (no lock/token/BGL anywhere in ng_ppp.c): the frags TAILQ, qlen, mseq/xseq, activeLinks and conf are mutated by ng_ppp_mp_input/frag_process/get_packet/frag_trim/frag_checkstale/frag_reset/rmnode without any serialization, and v1 (unlike netgraph7s cpu0 item funnel, DF-0330) has structurally concurrent execution contexts: cpu0-netisr control plane (ngc_send, uipc_socket.c:259 port), cpuN-netisr ksocket upcalls (inline in sowakeup, uipc_socket2.c:604), the BGL-held 0.5s fragTimer (plain callout_init => kern_timeout.c:580 get_mplock), and process-thread ng_iface output (no BGL, no netisr). Extensive adversarial attempts on the stock INVARIANTS guest did NOT reproduce corruption: 4000+2000 SET_CONFIG-toggle frag_reset races at ~700k deliveries/s across CPU-hashed pump flows, 700 shutdown-vs-inflight races, and 1600 dup-hammer timer-stale cycles over 8 staggered bundles all ran clean. The negative result is explained by undocumented funnels: connected-socket ksocket feeds forward delivery through the sockets cpu0 protocol port (and the soclose token handshake serializes shutdown), so every harness-reachable inbound pair executes serialized on stock topologies -- mirroring netgraph7s DF-0330 latent verdict. The defect remains certain by inspection and is one refactor (MPSAFE soreceive, port selection, any non-socket feeder such as ng_ether-fed PPPoE bundles with multi-CPU input hashing) away from racing; primitive if raced: double TAILQ_REMOVE/kfree of struct ng_ppp_frag + double m_freem (reclaim-exploitable heap corruption). Fix authored and regression-tested (per-node lwkt_token), not race-validated because no collision was achievable.",
  "exploit_chain": "",
  "evidence": [
    "findings/poc/DF-3481/VERDICT.md \u2014 full containment analysis and attempt log",
    "findings/poc/DF-3481/pppfragrace.c \u2014 8-bundle sticker/park/stale/dup-hammer timer race harness",
    "findings/poc/DF-3481/pppteardown.c \u2014 shutdown (mode 0) and SET_CONFIG-toggle (mode 1) race harness",
    "findings/poc/DF-3481/run.log \u2014 timer-race cycles completing without panic",
    "findings/poc/DF-3481/run.teardown.log \u2014 teardown attempts completing without panic",
    "findings/poc/DF-3481/fix_regression.log \u2014 patched ng_ppp.ko (per-node token) survives toggle storm + frag cycles",
    "findings/poc/DF-3481/fix.diff \u2014 per-node lwkt_token serialization (compiles -Werror)"
  ],
  "kernel_refs": [
    "sys/netgraph/ppp/ng_ppp.c:1144",
    "sys/netgraph/ppp/ng_ppp.c:1210",
    "sys/netgraph/ppp/ng_ppp.c:1290",
    "sys/netgraph/ppp/ng_ppp.c:1364",
    "sys/netgraph/ppp/ng_ppp.c:1447",
    "sys/netgraph/ppp/ng_ppp.c:1876",
    "sys/netgraph/ppp/ng_ppp.c:791",
    "sys/netgraph/netgraph/ng_base.c:2034",
    "sys/kern/kern_timeout.c:580",
    "sys/kern/uipc_socket.c:259",
    "sys/kern/uipc_socket2.c:604",
    "sys/netgraph/ksocket/ng_ksocket.c:1097"
  ],
  "poc_changes": "Initial design parked stale packets behind a first-only blocker with sequence gaps; that is invisible to ng_ppp_frag_checkstale because its running-seq scanner desyncs at any gap (the DF-0386 defect) -- final design pins mseq with a middle-frag sticker on a second link and uses strict head-contiguity. Dup-seq hammer fragments walk the full queue (TAILQ_FOREACH_REVERSE in ng_ppp_mp_input) without inserting, so only the timer delivers stale batches. AF_NETGRAPH sockaddr paths longer than ~14 chars are truncated by the socket layer (sendto returns ENOENT); all node/hook names were shortened accordingly. mkpeer must target the OWNING node (f1:), not the peer path. Leftover graphs hold UDP ports forever, so ports are allocated dynamically each run.",
  "attempts": 9,
  "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": 5400,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -w -pthread -o pppfragrace pppfragrace.c; cc -O2 -w -pthread -o pppteardown pppteardown.c; kldload ng_socket ng_ppp ng_ksocket",
  "run_cmd": "./pppfragrace 200; ./pppteardown 4000 1",
  "code_hash": "0507441deb7ab23e7fc8445cbcb13c6359c52f47b978848956e54c504b23303f",
  "notes": "Side discovery for the orchestrator (out of this files scope): ng_iface and ng_eiface node creation panics the stock kernel deterministically (panic: trying to free NULL pointer in ng_iface_constructor+0x3a2 / ng_eiface_constructor+0xf6 via plain root ngctl mkpeer . iface i0 inet on a fresh boot) -- logs preserved in findings/poc/DF-3482/. Both node types are unusable on this kernel, which also blocked the ng_iface-output race topologies. Guest was reset with-src after fix validation.",
  "recommended_fix": "Add a per-node lwkt_token to ng_ppp_private acquired at every entry point (rcvmsg/newhook/rcvdata/rmnode/disconnect/frag_timeout) -- see fix.diff; porting netgraph7s per-node writer token is the long-term fix.",
  "fix_status": "not_testable",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: (ng_ppp.ko rebuilt in-guest with per-node token; netgraph.ko rebuilt with DF-3483 fix)",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched module compiles (-Werror) and survives 1000 config-toggle storms + 160 frag-race cycles with the data path functional (fix_regression.log). Race-validated fix is not possible because the baseline race never collided on stock topologies (socket-port funnel).",
  "fix_evidence": "findings/poc/DF-3481/fix_regression.log"
}