# DF-3526 VERDICT — REPRODUCED (panic, deterministic 3/3, root-only)

## Bottom line

Creating the first netgraph7 `ng_iface` node on the stock kernel panics
instantly and 100% deterministically: `panic: trying to free NULL pointer`
from `ng_iface_constructor+0x312`, reached through the inlined
`ng_iface_get_unit()` (sys/netgraph7/iface/ng_iface.c:292-303).
Root cause is a **compiler-visible UB pattern**, proven not a stale
binary: `bcopy(ng_iface_units, ...)` at ng_iface.c:296 feeds
`__builtin_memmove()` a pointer that is NULL on the first call; per the
in-tree contract (sys/sys/systm.h:257-260) GCC then assumes the pointer
non-NULL and deletes the `if (ng_iface_units != NULL)` guard at :300, so
the compiled module calls `kfree(NULL)` unconditionally on the
array-growth path — which is exactly the first-creation path.
`kfree(NULL)` is an unconditional panic (sys/kern/kern_slaballoc.c:1406),
so non-INVARIANTS production kernels are equally affected. This is the
ng7 twin of v1 DF-3485, independently verified against the ng7 module set.

## How it was proven

1. **Machine code, freshly built** (disasm.stock.txt, reloc.stock.txt,
   GCC 8.3 DragonFly): in `ng_iface_constructor`, the load of
   `ng_iface_units` at +0xd4d flows through `memmove` (reloc at +0xd76)
   into `call _kfree` (reloc at +0xdce) with **no NULL test in between**.
   The panic return address `+0x312` matches this exact call.
2. **Live reproduction, 3/3 across independent `vm.sh reset with-src`
   boots** with freshly built stock modules:
   `./iface_con create` (root, ng7 control socket, NGM_MKPEER type
   "iface") → guest dead every time with the identical trace
   (panic.txt, run.log, run.2.log, run.3.log):
       _kfree+0x558 <- ng_iface_constructor+0x312
       <- ng_make_node+0x5f <- ng_apply_item+0x666 <- ngthread+0x18
3. **Trigger is root-only**: ng7 ng_socket control-socket attach is
   gated by `caps_priv_check(SYSCAP_RESTRICTEDROOT | __SYSCAP_NULLCRED)`
   (sys/netgraph7/socket/ng_socket.c:182-185); uid 1001 gets EPERM
   (unpriv_check.txt). Severity: Low (privileged deterministic DoS).
4. **Fix validation** (fix.diff, module rebuild only, no kernel rebuild):
   disasm.fix26.txt shows a real `test %r10,%r10 / je` now dominating the
   kfree, and the `ng_iface_units_len > 0` guard around the bcopy.
   Live: full lifecycle succeeds — `mkpeer` survives, GET_IFNAME
   returns "ng0", NGM_SHUTDOWN clean; 20/20 serial + 2 parallel
   create/destroy cycles, zero panics (fix_run.log).

## The DF-3486 half of the lead is REFUTED for ng7

With only the bcopy fix applied (DF-3526), node creation proceeds to
`if_attach(ifp, NULL)` (ng_iface.c:624) and **succeeds** — no
"try holding ifnet lock in netisr" panic, no deadlock. Mechanically:
`ng_snd_item()` queues **every first-hop item** (`refs == 1` — which is
every control message entering netgraph from ng_socket) to cpu0's
netgraph port: `lwkt_sendmsg(ng_cpuport(0), msg)` (ng_base.c:1956-1961),
where the dedicated `ngthread` (a kernel thread, not a netisr) applies
it. The DF-3486 v1 panic came from v1's control path running on a
TD_TYPE_NETISR thread; ng7's thread model differs (exactly the audit
lead's "token model differs" hint), and in ngthread context
`if_attach()`'s ifnet lock and `netmsg_service_sync()` are legal.
Empirical + mechanical: ng7 ng_iface does NOT share DF-3486.

Side benefit: because all ng7 control messages (constructors and
shutdowns alike) funnel through the single cpu0 ngthread, the
unsynchronized `ng_iface_units` bitmap is serialized by construction —
the parallel create test (fix_run.log) exercised exactly that.

## Impact characterization

`kfree(NULL)` is caught by the allocator's explicit NULL check before
any memory is touched — a clean, deterministic panic (DoS). There is no
write primitive, no double-free, no corruption: the stop is inside
kfree's argument validation. No escalation path exists from this bug.

## Relation to prior findings

- DF-3485 (v1 sys/netgraph/iface/ng_iface.c): same root cause, v1
  module set; verified 3/3 there too. DF-3526 is the independent ng7
  copy — separate file, separate module, separate fix commit.
- DF-3486: NOT shared (see above).
- DF-0607 (BPF write uninit sa_data on the same ng7 file): unrelated,
  not re-reported here.

## 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.
