⬢ DragonFlyBSD Kernel Audit
DF-3527 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3527",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "ng_iface_rcvmsg() returns EBUSY early at ng_iface.c:666 (interface IFF_UP) skipping the kfree(msg) at :732; the v1 netgraph contract makes rcvmsg the owner of the message (ng_base.c:1178, CALL_MSG_HANDLER :1186-1200) and the sender never frees it (ng_socket.c:253-258 The callee will free the xmsg when done), so every NGM_IFACE_POINT2POINT/NGM_IFACE_BROADCAST sendto() leaks sizeof(ng_mesg)+arglen+1 bytes of M_NETGRAPH heap. Reproduced live on the guest: 2048 EBUSY replies -> netgraph allocator 2.01K/40.0M -> 4.01K/80.0M (exactly +2048 allocations), accumulating linearly 40M->80M->120M across runs and never returning (kmem limit 390M); control run on the error-free GET_IFNAME path shows zero growth. Root-gated (control socket requires SYSCAP_RESTRICTEDROOT, ng_socket.c:172-176) hence Low severity; unbounded repeatable kernel-memory-exhaustion DoS. PoC required DF-3485+DF-3486 validated fixes in-guest only so the ng_iface node can be created at all (stock panics at ng_iface.c:605 - re-verified live this session, panic.iface_residual_df3486.txt).",
  "exploit_chain": "root (or any future unprivileged netgraph control path): socket(PF_NETGRAPH,SOCK_DGRAM,NG_CONTROL) -> sendto(ng0:, ng_mesg{NGM_IFACE_COOKIE, NGM_IFACE_POINT2POINT, arglen=N}) repeated while ng0 is UP -> each send leaks 52+arglen+1 bytes -> ~8K messages with 64K arglen exhaust the 390M kmem limit -> kernel memory starvation DoS. No memory corruption: pure allocation leak.",
  "evidence": "run.log (control 0-growth vs leak +2048 allocs/+40MB, EBUSY=2048/2048), run.2.log (linear accumulation 80M->120M), fix_run.log (fixed module: EBUSY=2048/2048 but 6.01K/120M FLAT), fix_build.log, panic.iface_residual_df3486.txt (DF-3486 residual at ng_iface.c:605 verified), env.txt, leak.c, leak_trigger.sh, fix.diff",
  "kernel_refs": [
    "sys/netgraph/iface/ng_iface.c:666",
    "sys/netgraph/iface/ng_iface.c:732",
    "sys/netgraph/netgraph/ng_base.c:1176",
    "sys/netgraph/netgraph/ng_base.c:1186",
    "sys/netgraph/socket/ng_socket.c:253",
    "sys/netgraph/socket/ng_socket.c:256",
    "sys/netgraph/socket/ng_socket.c:172"
  ],
  "poc_changes": "node address must be absolute ng0: (bare name = relative hook path -> ENOENT); v1 ng_mesg built locally (arglen is u_int16_t in v1, NG_VERSION=2, NG_CONTROL=2, SOCK_DGRAM); drain synchronous response after each success (else socket rcv buf fills -> ENOBUFS); vmstat -m parsed for InUse/MemUse columns",
  "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": 1320,
  "guest_dirty": 1,
  "build_cmd": "cc -O2 -Wall -o /tmp/df3527_leak /tmp/df3527_leak.c   (in guest; module prebuilt: cd /usr/src/sys/netgraph/iface && make)",
  "run_cmd": "sh /tmp/df3527_leak_trigger.sh   (creates nothing; ng0 must exist and be UP: ngctl mkpeer . iface inet inet; ifconfig ng0 up)",
  "code_hash": "e1245582bb4b367710c915118ceb48f2aa72abb4dba8fd612563df8002b1a142",
  "notes": "Requires DF-3485+DF-3486 validated fixes applied in-guest (module-only) for the ng_iface node to be creatable at all; those fixes do not touch rcvmsg. DF-3486 residual state at ng_iface.c:605 re-verified live this run (see panic.iface_residual_df3486.txt). Side-fact for the mppc clean verdict: stock ng_mppc.ko does not kldload on the stock kernel (interface rc4.1 already present in the KLD kernel - module Makefile links rc4.c/sha1.c while kernel also compiles crypto/rc4/rc4.c, sys/conf/files:2268); packaging tangle, not a memory-safety defect.",
  "recommended_fix": "Replace the early return (EBUSY) in ng_iface_rcvmsg with error = EBUSY; goto done; so the function epilogue frees the message (fix.diff).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (same kernel; module-only swap: ng_iface.ko with DF-3485+DF-3486+this fix)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Module-swap A/B on the same kernel: baseline module leaks +2048 allocs/+40MB per 2048-message run (persistent, accumulating); module with fix.diff returns identical EBUSY semantics with ZERO allocator growth (6.01K/120M flat across control and leak runs). Leak eliminated; no behavior change.",
  "fix_evidence": "fix_build.log (patch applied hunk#1@758 hunk#2@823, module relinked), fix_run.log (EBUSY=2048, InUse/MemUse unchanged vs control), run.log+run.2.log (baseline accumulation 40M->80M->120M)"
}