DF-2587 / harness_mod.c
/* * DF-2587 harness module โ deterministically confirms the ng_ether NULL deref. * * The three if_ethersubr callbacks ng_ether_input/ng_ether_input_orphan/ * ng_ether_output deref IFP2NG(ifp) as node->private with NO NULL check * (sys/netgraph/ether/ng_ether.c:206/222/260), unlike ng_ether_detach which * guards `if (node == NULL) return;` (:326). IFP2NG(ifp) is NULL on a live * interface only via ng_ether_attach failure (memory pressure) or a detach * race (interface destroyed during in-flight traffic) โ both hard to hit * reliably from userspace (ifconfig destroy returns EBUSY while the tap fd is * open, and netisr drains bursts before detach). * * This module sets up the EXACT post-race / failed-attach state on the live * vtnet0 interface โ IFP2NG(vtnet0) := NULL โ so the very next packet on * vtnet0 exercises the unguarded deref. On load it nulls ac_netgraph; the * driver then sends any packet (e.g. `ping 10.0.2.2`) -> ether_output -> * ng_ether_output(vtnet0) -> node = IFP2NG(vtnet0) = NULL -> node->private * -> NULL deref -> panic. This is function-level primitive confirmation * (analogous to harness-only findings). * * On a FIXED ng_ether.ko (NULL check in all three callbacks) the same setup * does NOT crash: ng_ether_output sees node==NULL and returns 0; ping works. * * IFP2NG(ifp) == ((struct arpcom *)ifp)->ac_netgraph (ng_ether.c:68-69, * arpcom has struct ifnet ac_if as its first member, so the cast is valid). * * Build/run (root): see build_mod.sh / run_mod.sh */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_arp.h> static struct ifnet *saved_ifp; static void *saved_ng; static int harness_load(module_t mod, int what, void *arg) { struct arpcom *ac; struct ifnet *ifp; switch (what) { case MOD_LOAD: ifnet_lock(); /* ifunit() requires ifnet_mtx */ ifp = ifunit("vtnet0"); if (ifp == NULL) { ifnet_unlock(); kprintf("DF2587: vtnet0 not found\n"); return ENXIO; } ac = (struct arpcom *)ifp; /* ac_if is first member of arpcom */ saved_ifp = ifp; saved_ng = ac->ac_netgraph; ac->ac_netgraph = NULL; /* IFP2NG(vtnet0) := NULL */ ifnet_unlock(); kprintf("DF2587: IFP2NG(vtnet0) set NULL (was %p). " "Next packet on vtnet0 -> ng_ether_output/input NULL deref.\n", saved_ng); return 0; case MOD_UNLOAD: ifnet_lock(); if (saved_ifp != NULL) { ((struct arpcom *)saved_ifp)->ac_netgraph = saved_ng; kprintf("DF2587: restored IFP2NG(vtnet0)=%p\n", saved_ng); } ifnet_unlock(); return 0; } return 0; } static moduledata_t df2587_harness = { "df2587_harness", harness_load, NULL }; DECLARE_MODULE(df2587_harness, df2587_harness, SI_SUB_PSEUDO, SI_ORDER_ANY); |