# DF-4114 — VERDICT

**Finding**: In TBI mode `nge_attach` sets `sc->nge_miibus = dev`
(sys/dev/netif/nge/if_nge.c:864); `nge_detach` then calls
`device_delete_child(dev, sc->nge_miibus)` = `device_delete_child(dev,
dev)` (if_nge.c:941-942), which newbus does not support.

**Status**: `untested`.  Hardware-gated: requires a TBI-mode DP83820
(`NGE_CFG_TBI_EN`, no external PHY) plus root (detach path) or an
IRQ-allocation failure at boot (attach-fail path).  The audit guest has
no NS DP83820 at all (presence_probe.sh: NGE_ABSENT), so neither branch
can be entered.  Source-level certainty — traced step by step below.

**Path A — normal detach (kldunload if_nge, devctl): unbounded
recursion → panic.**
1. kldunload → `device_detach(dev)` with `dev->state == DS_ATTACHED`
   (sys/kern/subr_bus.c:2136).
2. `DEVICE_DETACH(dev)` → `nge_detach(dev)`; state is *still*
   DS_ATTACHED (it becomes DS_NOTPRESENT only at subr_bus.c:2149, after
   the method returns).
3. `nge_detach` reaches if_nge.c:941-942 → `device_delete_child(dev,
   dev)` → subr_bus.c:1292 `device_detach(dev)` → still DS_ATTACHED →
   `DEVICE_DETACH(dev)` → `nge_detach` again → same call → infinite
   recursion.  Each frame re-runs the attached block: second
   `bus_teardown_intr` (stale cookie), second `ether_ifdetach` on an
   already-detached ifnet, `contigfree` of `nge_ldata` and the jumbo
   arena a second time (if_nge.c:949-954) — double-free/list corruption
   panics within a few recursion levels; unbounded stack otherwise.

**Path B — attach-failure path (`nge_attach` `goto fail` → direct
`nge_detach` at if_nge.c:921, e.g. bus_setup_intr failure at
:913-916 after the TBI branch): live device freed → newbus UAF.**
1. `dev->state == DS_ATTACHING` during the direct call, so
   `device_is_attached(dev)` is false and the stop/teardown block is
   skipped; `sc->nge_miibus == dev` is non-NULL → `device_delete_child
   (dev, dev)`.
2. Nested `device_detach(dev)` returns 0 immediately
   (subr_bus.c:2136-2137: state != DS_ATTACHED → 0).
3. grandchild loop empty (TBI mode added no children) →
   `devclass_delete_device` (removes the live device from the nge
   devclass), `TAILQ_REMOVE(&dev->children, dev, link)`
   (subr_bus.c:1304) — `dev` is not a member of its own children list;
   the per-entry tqe links make this silently unlink `dev` from the
   *parent PCI bus* children list mid-attach — then
   `kobj_delete((kobj_t)dev, M_BUS)` (subr_bus.c:1306) **frees the
   live device**.
4. `device_attach`/probe machinery continues on the freed device_t
   (state stores, devname printf, sysctl) — use-after-free read/write
   in newbus.

**Impact ceiling**: privileged DoS with certainty (panic/UAF); on
TBI-equipped systems it also bricks driver re-probing for the slot.
Not remotely triggerable; no unprivileged path.

**Fix**: never pass the self-reference to newbus —
`if (sc->nge_miibus && sc->nge_miibus != dev)` (fix.diff; validated as
text against the read-only tree, patch --dry-run clean; runtime
validation impossible without a TBI DP83820 → fix_status
`not_tested`).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
