DF-4342 / verdict.json
{ "finding_id": "DF-4342", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Deterministic logic defect, verified line-by-line against the source; not exercisable on the audit guest because the mn netgraph surface cannot exist there (no 0x110a PCI device, if_mn optional mn netgraph absent from GENERIC, kldload ENOENT). ngmn_newhook admits a second hook on an existing channel whenever its state is DOWN (if_mn.c:470-473), silently overwriting sc->ch[chan]->hook/ts (474-475) and double-incrementing nhooks (478); ngmn_connect then reports silent success for the second hook (664-665); the first ngmn_disconnect tears the shared channel down under the still-connected second hook (756-794) whose own disconnect early-returns before the nhooks decrement (756-758 vs 795), permanently wedging ngmn_config line changes (308-310) and blackholing the second hooks traffic. Two distinct hook names provably alias one channel (E1: ffs(ts)-1 for ts2 vs ts2-3; E1U: hardcoded chan=1) and the netgraph core keys hooks by name only (ng_base.c:697-763). Concurrent rmhook of the two aliased hooks double-frees both descriptor rings (no serialization; mn_free_desc clobbers dp->vnext with the freelist head at if_mn.c:498-503, so the losing walker frees live descriptors of other channels).", "exploit_chain": "root ngctl: mkpeer ts2; mkpeer ts2-3 (aliases chan 2, accepted while DOWN); connect both (second silent); rmhook ts2 -> full teardown under live ts2-3 hook; ts2-3 blackholed; textconfig line e1u refused forever (nhooks stuck). Concurrent variant: rmhook ts2 & rmhook ts2-3 & -> both teardowns pass the DOWN check -> double-free of every trxd + m_freem of every mbuf -> M_MN pool corruption. Not runnable on this guest (hardware absent).", "evidence": "VERDICT.md (7-step deterministic trace); README.md (code excerpts); trigger_alias.sh; run.log + env_guest.txt (surface absent); fix.diff (hook-ownership guard, git apply --check OK)", "kernel_refs": [ "sys/dev/netif/mn/if_mn.c:470", "sys/dev/netif/mn/if_mn.c:474", "sys/dev/netif/mn/if_mn.c:475", "sys/dev/netif/mn/if_mn.c:478", "sys/dev/netif/mn/if_mn.c:466", "sys/dev/netif/mn/if_mn.c:664", "sys/dev/netif/mn/if_mn.c:756", "sys/dev/netif/mn/if_mn.c:758", "sys/dev/netif/mn/if_mn.c:795", "sys/dev/netif/mn/if_mn.c:308", "sys/dev/netif/mn/if_mn.c:498", "sys/netgraph/netgraph/ng_base.c:697" ], "poc_changes": "trigger written fresh (no seed)", "attempts": 1, "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": 18, "guest_dirty": 0, "build_cmd": "n/a (kernel driver, optional mn netgraph; not present on guest)", "run_cmd": "./run.sh guest (see run.log)", "code_hash": "", "notes": "Hardware-gated + root; deterministic logic corruption (nhooks drift, admin DoS, silent data blackhole) plus double-free enabler together with the missing serialization of DF-4341.", "recommended_fix": "Gate ngmn_newhook on sc->ch[chan]->hook != NULL instead of state==UP, and clear sch->hook in ngmn_disconnect (fix.diff; also closes DF-1540 at the writer side)", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "No baseline on this guest (hardware absent). fix.diff is line-accurate (git apply --check clean) and closes both the aliasing admission and the dangling sch->hook left behind (DF-1540 writer side).", "fix_evidence": "findings/poc/DF-4342/fix.diff" } |