DF-0735 / df735_trigger.c
/* * df735_trigger.c - minimal trigger for the assertion mechanism cited in DF-0735. * * The cited bug at sys/netgraph7/ng_ipfw.c:248 calls ip_input(m) directly from * ng_ipfw_rcvdata(), which runs in the netgraph worker thread created at * ng_base.c:2787-2789. That thread is a plain lwkt thread (NOT registered via * netmsg_service_port_init at netisr.c:258-280), so it is NOT a netisr thread. * The first line of ip_input() (ip_input.c:460) is ASSERT_NETISR_NCPUS(mycpuid), * a KASSERT that on default-GENERIC (INVARIANTS ON) fires the moment ip_input * is entered from any non-netisr caller. * * The real ng_ipfw.ko is dead code on this tree (see ng_df735_poc.c header for * why), so we cannot exercise the exact cited call site. To prove the * underlying mechanism, this module exposes a sysctl whose handler runs in the * *invoking thread* (which, like the netgraph worker thread, is not a netisr * thread) and calls ip_input(m). If the assertion is compiled in, this panics. * * Trigger: sysctl debug.df735_trigger=1 (as root) * * The mechanism is identical whether the caller is a sysctl-handler thread, * a kthread, or the netgraph worker thread -- all are non-netisr threads, * so all trip the same KASSERT. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/sysctl.h> #include <sys/malloc.h> #include <sys/types.h> #include <net/netisr.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_var.h> /* ip_input() is defined at sys/netinet/ip_input.c:445 but is not declared * in any public header (only ip_input_handler is wired into netisr at * ip_input.c:393). ng_ipfw.c gets an implicit declaration for the same * call (line 248); we declare it explicitly. */ extern void ip_input(struct mbuf *); static int df735_trigger(SYSCTL_HANDLER_ARGS); static int df735_armed = 0; SYSCTL_PROC(_debug, OID_AUTO, df735_trigger, CTLTYPE_INT | CTLFLAG_RW, NULL, 0, df735_trigger, "I", "DF-0735: write 1 to call ip_input() from this (non-netisr) thread"); static int df735_trigger(SYSCTL_HANDLER_ARGS) { int error, val = 0; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val) { struct mbuf *m; /* Build a minimal mbuf with packet header. The KASSERT at the * top of ip_input fires before any packet-body examination, * so the contents are irrelevant. */ m = m_gethdr(M_WAITOK, MT_DATA); if (m == NULL) return (ENOMEM); m->m_pkthdr.len = m->m_len = 0; df735_armed++; /* The cited call site -- sys/netgraph7/ng_ipfw.c:248 -- is * `ip_input(m);` executed from the netgraph worker thread. * This call is the same statement from a different (but also * non-netisr) thread context. */ ip_input(m); } return (0); } |