DF-3526 / verdict.json
{ "finding_id": "DF-3526", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Creating the first netgraph7 ng_iface node panics the stock kernel deterministically (3/3 independent boots): panic: trying to free NULL pointer at ng_iface_constructor+0x312. Root cause is the compiler-visible UB pattern in ng_iface_get_unit() (sys/netgraph7/iface/ng_iface.c:296-301): bcopy() of the initially-NULL ng_iface_units maps onto __builtin_memmove, GCC deletes the NULL guard at :300, and the first array growth calls kfree(NULL), which is an unconditional panic even on non-INVARIANTS kernels (kern_slaballoc.c:1406). Proven live and by disassembly of the freshly-built module (reloc +0xd76 memmove -> +0xdce _kfree, no intervening test). ng7 twin of v1 DF-3485; separate file and module set. Trigger is root-only (ng7 ng_socket ngc_attach requires caps_priv_check(SYSCAP_RESTRICTEDROOT), uid 1001 gets EPERM), so impact is a privileged deterministic DoS with no write primitive (kfree validates NULL before touching memory). fix.diff (guard the bcopy call) validated at module level: guard restored in machine code, full mkpeer/GET_IFNAME=ng0/shutdown lifecycle plus 20/20 serial and parallel create/destroy cycles clean.", "exploit_chain": "", "evidence": "panic.txt (trace _kfree <- ng_iface_constructor+0x312 <- ng_make_node <- ng_apply_item <- ngthread); disasm.stock.txt + reloc.stock.txt (memmove@+0xd76 flows into _kfree@+0xdce, no NULL test); run.log/run.2.log/run.3.log (3/3); unpriv_check.txt (EPERM for uid 1001); disasm.fix26.txt + fix_run.log (fixed module: guard restored, lifecycle + stress clean)", "kernel_refs": [ "sys/netgraph7/iface/ng_iface.c:292", "sys/netgraph7/iface/ng_iface.c:296", "sys/netgraph7/iface/ng_iface.c:300", "sys/sys/systm.h:257", "sys/kern/kern_slaballoc.c:1406", "sys/netgraph7/socket/ng_socket.c:182" ], "poc_changes": "None \u2014 harness written fresh for this finding (ng7 control-socket ABI adapted from the DF-3524 pattern); no seed PoC existed.", "attempts": 4, "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": "cd /usr/src/sys/netgraph7/netgraph && make; cd ../socket && make; cd ../iface && make; cc -O2 -o /root/poc/iface_con /root/poc/iface_con.c", "run_cmd": "kldload /usr/src/sys/netgraph7/netgraph/netgraph.ko; kldload /usr/src/sys/netgraph7/socket/ng_socket.ko; kldload /usr/src/sys/netgraph7/iface/ng_iface.ko; /root/poc/iface_con create", "code_hash": "17bee5bb523d4edafdb0c96e86edb4d5c4731549aaf3dcb6933af83e08006fd4", "notes": "The task leads SECOND half (v1 DF-3486 if_attach-from-netisr twin) was checked and REFUTED for ng7: ng_snd_item() funnels every refs==1 control message to cpu0s ngthread port (ng_base.c:1956-1961), a kernel-thread context where if_attach()/if_detach() are legal \u2014 empirically the full lifecycle works with only the bcopy fix (fix_run.log). This also serializes all ng7 iface constructors/shutdowns on one thread, so the unsynchronized ng_iface_units bitmap has no concurrent access today. Guest left in clean reset state.", "recommended_fix": "Guard the bcopy call with if (ng_iface_units_len > 0) so the NULL-pointer operand never reaches __builtin_memmove and GCC keeps the kfree NULL test (see fix.diff).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 (stock kernel; ng_iface module rebuilt with fix.diff only)" } |