⬢ DragonFlyBSD Kernel Audit
DF-3483 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3483",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "ng_generic_msg()s NGM_MKPEER/NGM_CONNECT/NGM_NAME/NGM_RMHOOK arglen-mismatch error paths return EINVAL without kfree(msg) (ng_base.c:1263, 1279, 1296, 1309), violating the handler-frees-message contract (ng_base.c:1177); neither caller frees on error (ng_send_msg:1226, ngintr:2062), so every failing generic control message permanently leaks its user-sized struct ng_mesg allocation. Root-gated like netgraph7s DF-3474 (control socket is SYSCAP_RESTRICTEDROOT, ng_socket.c:172): v1 has no NG_FREE_ITEM/item abstraction to share DF-3474s mark-only semantics (its queue entries are really freed), but this is the same family -- control-message leak on generic error paths. Reproduced with exact size correlation: 50k bad MKPEER sends leaked 1935/1861 free pages vs 1953 predicted (160B zone), 50k bad NAME sends leaked 1122/1154 vs 1172 predicted (96B zone), while the addressing-failure control phase was flat (104/-1 pages). 200k total bad sends leaked ~30MB permanently. Impact ceiling: unbounded kernel heap exhaustion by a root caller (kldload/fork/socket degradation) -- local DoS, no info leak, no corruption. Fix validated: kfree(msg) added at all four sites; patched netgraph.ko shows 11/1 and 0/0 page deltas (noise) on the same test.",
  "exploit_chain": "root (or compromised root daemon) -> 4-arglen-off-by-one control message on an AF_NETGRAPH control socket -> ng_generic_msg early-returns without freeing -> each send permanently leaks kmalloc(payload) -> unbounded kernel heap exhaustion (DoS). No uid=0 route: leak-only.",
  "evidence": [
    "findings/poc/DF-3483/run.log \u2014 baseline: good 104p / bad 1935p / bad2 1122p per 50k",
    "findings/poc/DF-3483/run.2.log \u2014 repeat baseline: good -1p / bad 1861p / bad2 1154p",
    "findings/poc/DF-3483/fix_run.log \u2014 patched netgraph.ko: bad 11p/1p, bad2 0p/0p \u2014 leak gone",
    "findings/poc/DF-3483/arglenleak.c \u2014 A/B/C trigger with free-page accounting",
    "findings/poc/DF-3483/fix.diff \u2014 4x kfree(msg) before the early returns"
  ],
  "kernel_refs": [
    "sys/netgraph/netgraph/ng_base.c:1263",
    "sys/netgraph/netgraph/ng_base.c:1279",
    "sys/netgraph/netgraph/ng_base.c:1296",
    "sys/netgraph/netgraph/ng_base.c:1309",
    "sys/netgraph/netgraph/ng_base.c:1177",
    "sys/netgraph/netgraph/ng_base.c:1218",
    "sys/netgraph/netgraph/ng_base.c:1669",
    "sys/netgraph/socket/ng_socket.c:172",
    "sys/netgraph/socket/ng_socket.c:253"
  ],
  "poc_changes": "none of substance; wrote the A/B/C trigger from scratch (no seed PoC). Measured via vm.stats.vm.v_free_count deltas after vmstat -m proved unreliable for module malloc types on this guest (same caveat as DF-3474).",
  "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": 1800,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -w -o arglenleak arglenleak.c",
  "run_cmd": "kldload ng_socket; ./arglenleak 50000",
  "code_hash": "e08b9edb1ccc88d05ecd84e8026d5cbd3786d277bec650af0598c7182d29826a",
  "notes": "Baseline runs on stock /boot/kernel modules; fix validation on in-guest-rebuilt netgraph.ko (patch applied to the guests /usr/src copy; never applied to the audit tree). Guest reset with-src afterwards. The same early-return-without-free pattern should be audited in every node types rcvmsg across sys/netgraph/ (out of this files scope).",
  "recommended_fix": "kfree(msg, M_NETGRAPH) before each of the four arglen-mismatch returns in ng_generic_msg() \u2014 see fix.diff.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: (netgraph.ko rebuilt in-guest with fix.diff, loaded on fresh boot; ng_socket/ng_ppp/ng_ksocket stock)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Same 3-phase A/B/C test on the patched module: bad 11/1 pages, bad2 0/0 pages (noise) vs baseline 1935/1861 and 1122/1154 \u2014 leak eliminated, good path unchanged.",
  "fix_evidence": "findings/poc/DF-3483/fix_run.log"
}