DF-3534 / verdict.json
{ "finding_id": "DF-3534", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED: ng7 ngt_shutdown() never drops the constructor node reference (the ng7 shutdown contract \u2014 cf. ng_pred1.c:351 \u2014 requires it; the v1 twin at sys/netgraph/tty/ng_tty.c:554 does ng_unref(sc->node)). Every netgraph-tty ldisc install/remove leaks one ng_node + ID-hash entry permanently, with nothing but TIOCSETD on/off (no hook, no data, no timer). Isolation: nodd +1/cycle, socksock flat, and with the 2-line NG_NODE_UNREF fix nodes are flat across nodd500 + 2x2000 stall cycles. Privileged trigger (SYSCAP_NONET_NETGRAPH); unbounded; pins the ng_tty module; feeds the DF-3514 rehash panic threshold exactly like DF-3513/DF-3515 did.", "exploit_chain": "root/capability holder flips a ptys line discipline to NETGRAPHDISC and back: one ng_node (~176B) leaked per flip, forever; module unload permanently blocked; ng_nodes count only grows, eventually crossing the ng7 hash-rehash threshold (DF-3514 panic on stock module builds).", "evidence": "isolation.log (nodd/nodata/socksock/full matrix); fix_validation.log (nodes flat with the fix); refcount ledger narrative in VERDICT.md (9 refs in, 8 out, orphan = creation ref); fix.diff (2 lines).", "kernel_refs": [ "sys/netgraph7/tty/ng_tty.c:553", "sys/netgraph7/tty/ng_tty.c:568", "sys/netgraph7/netgraph/ng_base.c:723", "sys/netgraph7/ng_pred1.c:351", "sys/netgraph/tty/ng_tty.c:554" ], "poc_changes": "n/a \u2014 harness written from scratch during DF-3531 verification (tty_leak2.c, 4 isolation modes).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Sep 14 09:24:11 UTC 2026 x86_64 (isolation) / #0 stock (baseline corroboration)", "runtime_sec": 900, "guest_dirty": 0, "build_cmd": "cc -O2 -o tty_leak2 tty_leak2.c", "run_cmd": "vmstat -m | grep netgraph_node ; ./tty_leak2 nodd 500 ; vmstat -m | grep netgraph_node", "code_hash": "36375e9f24eb9efd06032d45eb96dd2587a4930096ed05e11172980f6a7ed9b4", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Sep 14 09:24:11 UTC 2026 + module rebuilt with fix.diff", "fix_verdict": "2-line module fix (NG_NODE_SET_PRIVATE(node,NULL) + NG_NODE_UNREF(node) at ngt_shutdown tail, mirroring ng_pred1.c:350-351) validated in-guest: netgraph_node in-use flat at 2 across nodd-500 and 2x2000 stall-close cycles (baseline +1/cycle); module no longer pinned by node refs. The later drain-stress panic in the same session is DF-3531s layer-1/2 close-vs-fire race, not this change (nodd mode with no timers ran clean and flat).", "fix_evidence": "fix_validation.log: nodd 500 nodes 2->2; stall-close 2000 twice nodes 2->2->2; isolation.log for the pre-fix +1/cycle baseline.", "notes": "Discovered during DF-3531 Phase V; the two leaks stack on stock (items + nodes both +1/cycle). Guest reset to clean with-src snapshot after the run.", "recommended_fix": "Add NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); at the end of ng7 ngt_shutdown (sys/netgraph7/tty/ng_tty.c:568, after kfree(sc))." } |