DF-3536 / verdict.json
{ "finding_id": "DF-3536", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED (leak, certain): every ng7 cisco downstream-hook disconnect with the armed 10s keepalive ng_callout leaks one ng_item + one node reference because ng_uncallout (ng_base.c:3281-3292) frees the item only when callout_stop() > 0 and this kernels callout_stop returns 0 for inline-completed armed cancels (kern_timeout.c:886-890, DF-3515). 300-cycle leak runs grow M_NETGRAPH/M_NETGRAPH_NODE by exactly +300 allocs each (linear, monotone); the inet-hook control run is flat; kldunload ng_cisco is permanently refused (Device busy); a 3000-cycle run on stock modules additionally panicked the guest via the DF-3514 rehash feed (panic: hashdestroy: hash not empty in ng_make_node_common). Trigger requires a netgraph control socket (SYSCAP_RESTRICTEDROOT).", "exploit_chain": "root netgraph user repeats NGM_MKPEER(cisco,downstream)+NGM_RMHOOK cycles: +1 pinned ng_node and +1 leaked ng_item per cycle, unbounded kernel heap growth, permanent module-unload denial, and on stock modules a deterministic kernel panic once ng_nodes crosses the ID-hash rehash threshold (DF-3514 feed).", "evidence": "run.log (302 -> flat control -> 602 allocs, kldunload Device busy), env.txt (netgraph_node 602/100K in lockstep), panic.txt (stock-module hashdestroy panic), VERDICT.md root-cause with path:line", "kernel_refs": [ "sys/netgraph7/cisco/ng_cisco.c:219", "sys/netgraph7/cisco/ng_cisco.c:418", "sys/netgraph7/netgraph/ng_base.c:3248", "sys/netgraph7/netgraph/ng_base.c:3257", "sys/netgraph7/netgraph/ng_base.c:3281", "sys/kern/kern_timeout.c:886" ], "poc_changes": "wrote harness from scratch modeled on DF-3531s ABI header: fixed sockaddr_ng family type (DragonFly sa_family_t is __uint8_t, not u_short \u2014 first build sent skewed paths and failed with ENOENT/ENXIO); ng7 modules for measured runs built with the DF-3514 rehash hunks pre-applied (rehash_fix.diff) so the separately-filed rehash defect does not mask the leak differential.", "attempts": 3, "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": "sh build.sh (ng7 netgraph/socket/cisco modules + cc -O2 -o ngcisco7_leak ngcisco7_leak.c)", "run_cmd": "sh run.sh", "code_hash": "ba822b3cd0f423670fe3001d43dae1a9960b6bd802dd8e032c59586aece3660d", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Framework-level two-layer fix (kern_timeout.c PREVENTED reporting + ng_uncallout ng_free_item) already authored and stress-validated by DF-3531s pack on this same guest, including its documented dispatch-precision caveat; re-validating requires a kernel rebuild and would only repeat DF-3531s experiment.", "fix_evidence": "fix.diff mirrors DF-3531/fix.diff; see findings/poc/DF-3531/VERDICT.md Fix validation for the kernel-rebuild results and the double-free caveat under close-vs-fire racing.", "notes": "Modules for measured runs loaded from /usr/src/sys/netgraph7; guest reset with-src afterwards for the DF-3535 phase.", "recommended_fix": "ng_uncallout must free the orphaned timer item (ng_free_item) whenever the cancel completed, gated on a dispatch-precise PREVENTED report from callout_stop (kern_timeout.c early-return fix); see DF-3531." } |