DF-4432 / dfrnull.c
/* * DF-4432 replica — proves the sink of the if_sln.c NULL-mbuf RX bug. * * Bug chain (all path:line against this repo's sys/ tree): * 1. if_sln.c:913 / if_sln.c:926 m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp) * 2. sys/kern/uipc_mbuf.c:2246-2249 m_getl(M_NOWAIT) failure => m_devget returns NULL * 3. if_sln.c:915-919 / 928-937 if (m == NULL) { ierrors++; ... } -- and falls through * 4. if_sln.c:949 ifp->if_input(ifp, m, NULL, -1); -- UNCONDITIONAL, m == NULL * 5. sys/net/if_ethersubr.c:1494 M_ASSERTPKTHDR(m) (KASSERT only under INVARIANTS) * 6. sys/net/if_ethersubr.c:1502 m->m_len (NULL deref on GENERIC -> fatal trap) * * The ancestor driver rl(4) guards this exact pattern with * if_rl.c:1095-1096 if (m == NULL) continue; * before if_rl.c:1099 ifp->if_input(ifp, m, NULL, -1); * The sln(4) port dropped that guard. * * This module creates a transient IFF_UP ethernet interface and issues the * EXACT statement of if_sln.c:949 with the EXACT faulting argument * (m == NULL, the m_devget-failure value), from kldload context. * Expected result on X86_64_GENERIC: Fatal trap 12 page fault, vaddr ~0x18 * (offsetof(struct mbuf, m_len)), i.e. the identical panic a real sln(4) * RX interrupt would take when m_devget() fails under cluster exhaustion. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_types.h> #include <net/if_arp.h> #include <net/ethernet.h> static struct ifnet *dfrn_ifp; static int dfrn_modevent(module_t mod, int type, void *arg) { static const uint8_t lla[ETHER_ADDR_LEN] = { 0x02, 0xdf, 0x44, 0x32, 0x00, 0x01 }; struct mbuf *m = NULL; /* m_devget() failure value, if_sln.c:915/928 */ switch (type) { case MOD_LOAD: dfrn_ifp = if_alloc(IFT_ETHER); if (dfrn_ifp == NULL) return (ENOMEM); if_initname(dfrn_ifp, "dfrn", 0); dfrn_ifp->if_mtu = ETHERMTU; /* sln_rx() only ever runs with the interface UP+RUNNING * (if_sln.c:1075 gates the interrupt on IFF_RUNNING; * if_sln.c:721 sets it in sln_init), so replicate IFF_UP. */ dfrn_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_UP; ether_ifattach(dfrn_ifp, lla, NULL); kprintf("DF-4432 replica: if_sln.c:949 -> " "if_input(ifp, NULL, NULL, -1) on dfrn0 (UP)\n"); ifnet_serialize_all(dfrn_ifp); /* ---------------- EXACT replica of if_sln.c:949 ---------------- */ dfrn_ifp->if_input(dfrn_ifp, m, NULL, -1); /* ----------------------------------------------------------------- */ ifnet_deserialize_all(dfrn_ifp); kprintf("DF-4432 replica: SURVIVED -- unexpected, bug refuted\n"); ether_ifdetach(dfrn_ifp); if_free(dfrn_ifp); return (0); case MOD_UNLOAD: return (0); default: return (EOPNOTSUPP); } } static moduledata_t dfrn_mod = { "dfrn", dfrn_modevent, NULL }; DECLARE_MODULE(dfrn, dfrn_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(dfrn, 1); |