⬢ DragonFlyBSD Kernel Audit
DF-3557 / verdict.json
← back to finding ↓ download raw
{
  "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)"
}