# DF-3502 — delport/rmnode never clear the member's ac_netgraph (IFP2NG); rmnode clears the shared ng_ether_input_p unconditionally → RX-frame UAF/NULL-deref panic landmine + multi-instance RX blackout

- File: `sys/netgraph/fec/ng_fec.c` (v1 netgraph, LIVE)
- Lines: 379 (set `ac->ac_netgraph = priv->node`), 416-471 (delport: no
  clear), 739-783 (`ng_fec_input` deref), 1132-1134 (constructor arms the
  shared global), 1233-1234 (rmnode clears it unconditionally)
- Severity: Medium (kernel UAF/NULL-deref panic triggered by any inbound
  ethernet frame after a recreate cycle; secondary: total RX blackout for
  coexisting fec bundles and ng_ether(4), which shares the global)

## Defects

1. `ng_fec_addport` stores the node pointer in the member's
   `arpcom.ac_netgraph` (IFP2NG), but `ng_fec_delport` and `ng_fec_rmnode`
   never clear it. After teardown (`kfree(priv)`, `ng_unref` frees the
   node) the member keeps a dangling pointer.
2. `ng_fec_rmnode` clears the global `ng_ether_input_p` unconditionally —
   even when other fec nodes (or ng_ether, which uses the same global) are
   still using it. Any later fec constructor re-arms it
   (`if (ng_ether_input_p == NULL)`), at which point
   `ether_input_frame` → `ng_fec_input` runs for **every** ethernet frame
   and dereferences `IFP2NG()` of previously-enslaved members → freed
   node → `node->private` (NULL or reused) → NULL+offset read (measured
   offset 0x3f8 = `offsetof(struct ng_fec_private, fec_bundle)`) or wild
   list walk → kernel panic in RX/interrupt context.

## Reproduction (run.sh)

    sysctl -n debug.fecdrive=create            # fec node f0
    sysctl -n debug.fecdrive="add vtnet0"      # ac_netgraph(vtnet0) = f0-node
    # detached (teardown downs vtnet0, ssh dies):
    sysctl -n debug.fecdrive=shutdown          # f0 freed; global cleared;
                                               # ac_netgraph dangles
    sysctl -n debug.fecdrive=echo              # echo node REUSES f0's chunk
                                               # (same address, private=NULL)
    sysctl -n debug.fecdrive=create            # new fec node re-arms global
    ifconfig vtnet0 up; ping 10.0.2.2          # first inbound frame:

    Fatal trap 12: page fault while in kernel mode
    fault virtual address = 0x3f8
    current process = Idle                     (RX/interrupt context)
    Stopped at ng_fec_input+0x30: movq 0x3f8(%rbx),%rax

The detonation needs no privilege once the dangling state exists: any
inbound frame on the member (external traffic, or any local user's traffic
that elicits a response). Deterministic across chunk-reuse outcomes: no
reuse → `private` reads NULL → fault at 0x3f8; reuse by a private-less
node (echo) → same; reuse by a node with a live `private` → foreign bundle
walk (wild deref or silent blackhole).

## Fix (validated)

- delport: `if (ac->ac_netgraph == (void *)priv->node) ac->ac_netgraph = NULL;`
- rmnode: only clear `ng_ether_input_p` when the last fec node goes away
  (armed counter) and only if the global still points at `ng_fec_input`.

Validated in-guest: identical sequence (shutdown → echo reuse → create →
ifconfig up → ping) completes with **no panic**, 3/3 pings, guest healthy.
