⬢ DragonFlyBSD Kernel Audit
DF-3502 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3502",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "ng_fec_delport/ng_fec_rmnode never clear the members ac_netgraph (IFP2NG) while addport stores the node pointer there, and rmnode clears the shared ng_ether_input_p global unconditionally. After any fec teardown the member keeps a dangling node pointer; the next fec node creation re-arms the global, and the next inbound ethernet frame on that member runs ng_fec_input over freed memory: reproduced deterministically as a page fault at address 0x3f8 (priv==NULL, offsetof(fec_bundle)=0x3f8) in RX/interrupt context (Idle process), with the freed node chunk provably reused by an echo node at the identical address. Secondary facet: rmnodes unconditional clear blinds every other live fec bundle and ng_ether(4), which shares the same global. Fixed and validated: with delport clearing IFP2NG and the global clear made reference-counted, the identical detonation sequence survives (3/3 pings, no panic).",
  "exploit_chain": "create fec node -> add_iface <ether member> (IFP2NG= node) -> shutdown node (member left with dangling IFP2NG; shared global cleared) -> create any new fec node (global re-armed) -> first inbound frame on the member: ng_fec_input dereferences freed node -> NULL+0x3f8 read -> kernel panic",
  "evidence": "panic.txt (Fatal trap 12, fault VA 0x3f8, Stopped at ng_fec_input+0x30, current process Idle); run.log (step-by-step with node addresses 0xfffff80117ab2840 freed then reused by echo node at the same address); run.sh; fixes.sh + fix.diff",
  "kernel_refs": [
    "sys/netgraph/fec/ng_fec.c:379",
    "sys/netgraph/fec/ng_fec.c:464",
    "sys/netgraph/fec/ng_fec.c:752",
    "sys/netgraph/fec/ng_fec.c:1133",
    "sys/netgraph/fec/ng_fec.c:1233",
    "sys/net/if_ethersubr.c:1273"
  ],
  "poc_changes": "driven from thread context via fecdrive.ko (stock csock path blocked by DF-3499/DF-3500); lab enablers E1/E2/E3 documented in enablers.sh \u2014 none touch the defect",
  "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": 1800,
  "guest_dirty": 0,
  "build_cmd": "sh enablers.sh && sh fixes.sh   # HEAD source + E1-E3 + fix in /usr/src/sys/netgraph/fec",
  "run_cmd": "sh run.sh   # create + add vtnet0, then detached: shutdown; echo; create; ifconfig vtnet0 up; ping 10.0.2.2",
  "code_hash": "e8cf25ff47210caf1634a98849d42b0c3a21526ed2c514447dbbe92969d59bae",
  "notes": "State creation needs the netgraph control socket (root on stock DFly, e.g. admin teardown of a bundle); detonation needs no privilege (any inbound frame after the member is next upped). Grooming angle documented: reuse of the freed node chunk by a node with a live private makes ng_fec_input walk a foreign bundle list instead of faulting.",
  "recommended_fix": "delport: clear ac->ac_netgraph when it equals priv->node; rmnode: clear ng_ether_input_p only when the last fec node is destroyed and the global still points at ng_fec_input",
  "fix_status": "fixed",
  "fix_notes": "fix.diff applied in-guest and validated: identical sequence (shutdown -> echo chunk reuse -> create -> ifconfig up -> ping) completes with no panic, 3/3 pings, guest healthy"
}