DF-3557 / verdict.json
{ "finding_id": "DF-3557", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Reproduced on the stock INVARIANTS guest: two ng7 UI nodes wired into a closed downstream/upstream ring (mkpeer+mkpeer+rmhook+connect) plus one NGM_FLOW_COOKIE control message hook-addressed to \"u0:downstream\" (delivered with lasthook set, no wire consumed) makes ng_UI_rcvmsg relay the item synchronously forever; ng_snd_item applies forwarded items inline, each ng_apply_item pushes an LWKT tokref, and the fixed td_toks_array[32] trips the always-on KKASSERT: panic: assertion \"ref < &td->td_toks_end\" failed in lwkt_gettoken at lwkt_token.c:661 with recursive ng_apply_item frames; guest dies in DDB. Root-gated (uid 1001 gets EPERM at socket() creation), so severity Low (privileged DoS), same class/ceiling as DF-3540/3553. No memory-safety violation, no write primitive, no path to uid=0. fix.diff (atomic nesting counter, max 8, ELOOP) validated: patched module returns errno 62 and survives repeated injections.", "exploit_chain": "", "evidence": "panic.txt + console_full.txt (panic banner + recursive ng_apply_item trace); run.log (baseline trigger output up to the hang, then guest death); fix_run.log (patched: errno 62 ELOOP, guest up across repeated runs); unpriv_check.txt (uid 1001 EPERM at socket creation); ui_cycle.c / build.sh / run.sh to re-run; VERDICT.md for the full trace", "kernel_refs": [ "sys/netgraph7/UI/ng_UI.c:145", "sys/netgraph7/UI/ng_UI.c:148", "sys/netgraph7/UI/ng_UI.c:153", "sys/netgraph7/netgraph/ng_base.c:1961", "sys/netgraph7/netgraph/ng_base.c:3100", "sys/netgraph7/netgraph/ng_base.c:1804", "sys/netgraph7/netgraph/ng_base.c:3051", "sys/netgraph7/socket/ng_socket.c:301", "sys/netgraph7/socket/ng_socket.c:466" ], "poc_changes": "Wrote trigger from scratch (no seed existed for this file): ng7 control-socket-only harness modeled on the DF-3540 pack (NG_VERSION 8, AF_NETGRAPH 32, sockaddr_ng data at offset 2). Key discovery vs the lead: a pure-UI DATA cycle is impossible (degree-2 node, data socket only injects via its own nodes hooks), but hook-addressed CONTROL delivery (ng_address_path setting el_hook) injects into a closed ring; also required the rmhook step so U0 survives with one hook (UI self-destructs at zero hooks) before the closing connect.", "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": 1740, "guest_dirty": 0, "build_cmd": "cd /usr/src/sys/netgraph7/netgraph && make && cd ../socket && make && cd ../UI && make && cc -O2 -o /root/poc57/ui_cycle /root/poc57/ui_cycle.c", "run_cmd": "kldload netgraph.ko ng_socket.ko ng_UI.ko (built paths) && /root/poc57/ui_cycle", "code_hash": "0c4e86c3d3508d2bc6e536b5b1c7723238ff7ebfa9449df7eaa1c0e19c9b3945", "notes": "Guest reset (with-src) after the panic and after fix validation; DB files rows updated (ng7 UI audited; v1 UI + ng_bluetooth.c clean). Attempts=2: baseline panic run + patched run (plus one unpriv run and one repeat-injection run recorded in the logs). v1 twin audited negative: v1 rcvmsg frees all messages (no relay), v1 control delivery carries no hook, and closed v1 UI cycles have no injection port -- documented in VERDICT.md.", "recommended_fix": "Bound nested synchronous FLOW relaying in ng_UI_rcvmsg() with an atomic nesting counter (NG_UI_FLOW_HOPS_MAX=8) that returns ELOOP; see fix.diff", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline (stock ng_UI.ko): deterministic panic, guest dead. Patched (ng_UI.ko rebuilt with fix.diff, only module swapped): sendto() returns errno 62 (ELOOP) after 8 bounded relay hops, guest survives repeated injections with fresh node names. fix.diff was authored against the read-only sys/ tree and applied only to the guests /usr/src copy.", "fix_evidence": "fix_run.log (patched runs: ELOOP, GUEST_STILL_UP); run.log + panic.txt (baseline panic); fix.diff (the patch)" } |