# DF-3500 — ng_fec control plane violates the ifnet locking contract everywhere: INVARIANTS panics on create/teardown via any netgraph control path

- File: `sys/netgraph/fec/ng_fec.c` (v1 netgraph, LIVE module)
- Lines: 1132-1140 (constructor: `ng_ether_input_p`, `ether_ifattach`),
  342/411 (`ifnet_lock` in addport), 430/468 (`ifnet_lock` in delport),
  453-455 (delport: bare `if_ioctl` on member), 521-533 (`ng_fec_init`
  serialize dance), 549-557 (`ng_fec_stop` unbalanced
  `ifnet_deserialize_all`), 1222 (`ng_fec_stop` called from rmnode)
- Severity: Medium (deterministic INVARIANTS panics / lock-protocol
  violations; control plane is root-only on stock DFly)

## Three reproduced facets (stock code paths)

1. **Constructor in netisr** — every netgraph control message is processed
   in a `TD_TYPE_NETISR` thread; the constructor calls `ether_ifattach` →
   `if_attach` → `ifnet_lock()`, which KASSERTs `td_type != TD_TYPE_NETISR`
   (sys/net/if.c:3787-3788). Any `ngctl mkpeer fec …` on an INVARIANTS
   kernel panics (after the DF-3499 kfree(NULL) is fixed; at HEAD DF-3499
   fires first).
2. **rmnode → ng_fec_stop** — `ng_fec_stop()` opens with
   `ifnet_deserialize_all(ifp)` (marked "XXX", ng_fec.c:549) but is called
   from `ng_fec_rmnode` (ng_fec.c:1222) without the serializer held →
   `lwkt_serialize_exit` IS_SERIALIZED assertion panic on every node
   teardown.
3. **delport member ioctl** — `bifp->if_ioctl(bifp, SIOCSIFFLAGS, NULL,
   NULL)` (ng_fec.c:454-455) without `ifnet_serialize_all(bifp)` → the
   member driver's serializer assertion fires (reproduced with vtnet:
   `vtnet_serialize_assert` → panic). Same pattern in `ng_fec_init`
   (524-532) is at least serialize-balanced, but still runs from netisr.

## Run (facet 1)

    # module with only the DF-3499 bcopy guard applied (see DF-3499/fix.diff)
    cd /usr/src/sys/netgraph/fec && make
    kldload ng_socket; kldload ./ng_fec.ko
    ngctl mkpeer fec c0 c1
    -> panic: try holding ifnet lock in netisr
       ifnet_lock() <- if_attach() <- ether_ifattach_bpf() <- ng_fec_constructor()

Facets 2 and 3 are demonstrated with the fecdrive.ko thread-context driver
(needed because the stock csock path cannot get past facet 1 / DF-3499):
`sysctl -n debug.fecdrive=shutdown` after `create`+`add vtnet0` panics in
facet 2 with the stock `ng_fec_stop` line present; with that one call
lab-disabled, the same command panics in facet 3 at `vtnet_serialize_assert`
through `ng_fec_delport`. See panic_ctor_netisr.txt and
panic_rmnode_delport.txt.

## Notes

- On non-INVARIANTS production kernels these paths do not trip KASSERTs but
  still take `ifnet_mtx` (a sleep-capable mutex) and member serializers from
  netisr context — a lock-order / sleep-in-netisr hazard, not just debug
  noise.
- This finding is why the DF-3501/DF-3502 verifications use fecdrive.ko
  (kldload/sysctl thread context) plus three documented lab enablers.
