DF-3546 / verdict.json
{ "finding_id": "DF-3546", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "v1 ng_bpf accepts cyclic ifMatch/ifNotMatch configs (incl. ifMatch==thisHook) and the v1 core forwards synchronously with no hop bound (ng_send_data calls the peer rcvdata directly), so one injected packet recurses until the kernel stack guard page double-faults. Verified live on the stock INVARIANTS guest in both self and cross cycle modes: DOUBLE FAULT - KERNEL STACK GUARD HIT! / panic: double fault, rip inside ng_bpf.ko. Root-only trigger (v1 ngc_attach caps_priv_check; uid 1001 gets errno 43), so privileged DoS like the ng7 twin DF-3540.", "exploit_chain": "", "evidence": "panic.txt (double fault signature, rip=0xffffffff82656970 inside ng_bpf.ko), console_self.txt + console_cross_tail.txt (fresh boot then panic for each mode), run.self.log (program output stops at injection; ssh died with guest), unpriv_check.txt (uid 1001 EPROTONOSUPPORT), fix1_self.log + fix2_cross.log (patched module: EINVAL guard / ELOOP depth-drop, guest survives)", "kernel_refs": [ "sys/netgraph/bpf/ng_bpf.c:419", "sys/netgraph/bpf/ng_bpf.c:431", "sys/netgraph/bpf/ng_bpf.c:474", "sys/netgraph/netgraph/ng_base.c:1678", "sys/netgraph/netgraph/ng_base.c:1685", "sys/netgraph/socket/ng_socket.c:172" ], "poc_changes": "Adapted the ng7 DF-3540 trigger to the v1 user ABI: NG_VERSION 2 header layout (arglen u16 at offset 2, flags/token/typecookie/cmd order), NGM_GENERIC_COOKIE 851672668, synchronous control delivery (errors via errno, no NODEINFO round-trip needed), named socket node for the data-socket connect. Fixed one trigger bug found during verification: NGM_CONNECT con.path must be relative to the addressed node (p), not socket-relative (in.p) -- first cross attempt failed ENOENT.", "attempts": 2, "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": 2700, "guest_dirty": 0, "build_cmd": "cc -O2 -o /root/ng_cycle_v1 /root/ng_cycle_v1.c (in-guest, root; cc 8.3 [DragonFly])", "run_cmd": "kldload netgraph ng_socket ng_bpf; /root/ng_cycle_v1 self (then reset with-src; /root/ng_cycle_v1 cross)", "code_hash": "f31b0e82e730865da7117a3b38f8a873cf33176bd79cd568d481498a7f3b9f0a", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src only; ng_bpf.ko rebuilt -Werror-clean and loaded. Stock: self=panic, cross=panic. Patched: self -> SET_PROGRAM EINVAL (thisHook guard), cross -> sendto ELOOP at depth 16, guest survives 30s in both. fix.diff never applied to the audit sys/ tree.", "fix_evidence": "fix1_self.log (errno=22 EINVAL on x1->x1), fix2_cross.log (errno=62 ELOOP, RESULT no-panic survived 30s), fix_build.log (-Werror module build)", "notes": "v1 twin of DF-3540 (ng7): different failure mode -- v1 has no items/tokens/hop accounting at all, the recursion exhausts the kstack (guard-page double fault) instead of tripping the LWKT token-array KKASSERT. Verified DF-3444s unvalidated-filter primitive does NOT apply to v1: ng_bpf_setprog calls bpf_validate (ng_bpf.c:481) and this trees bpf_validate bounds BPF_MEM/ST/STX and rejects len<1/len>BPF_MAXINSNS (sys/net/bpf_filter.c:511-517).", "recommended_fix": "ng_bpf_setprog: reject ifMatch/ifNotMatch == thisHook (non-empty); bound recursive forwarding depth in ng_bpf_rcvdata (ELOOP past 16 hops) -- or better, add a hop bound to v1 ng_send_data itself." } |