/*
 * DF-5411 PoC harness — if_udav.c attach-failure DF-5334-class panic.
 *
 * Target: sys/bus/u4b/net/if_udav.c udav_attach() failure branches:
 *
 *   263:   error = usbd_transfer_setup(uaa->device, &iface_index,
 *   264:       sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_lock);
 *   265:   if (error) {
 *   266:       device_printf(dev, "allocating USB transfers failed\n");
 *   267:       goto detach;
 *   268:   }
 *   ...
 *   280:   ue->ue_sc = sc;          \
 *   281:   ue->ue_dev = dev;         \  assigned only AFTER the
 *   282:   ue->ue_udev = uaa->device; > failing usbd_transfer_setup()
 *   283:   ue->ue_lock = &sc->sc_lock;/
 *   285:   error = uether_ifattach(ue);
 *   286:   if (error) {
 *   287:       device_printf(dev, "could not attach interface\n");
 *   288:       goto detach;
 *   289:   }
 *   293: detach:
 *   294:   udav_detach(dev);
 *
 * udav_detach() (if_udav.c:298-309) then runs, in order:
 *
 *   304:   usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
 *   305:   uether_ifdetach(ue);
 *   306:   lockuninit(&sc->sc_lock);
 *
 * On the transfer-setup failure branch every ue field that
 * uether_ifdetach() needs is still ZERO (they are assigned only at
 * if_udav.c:280-283, AFTER the failing call).  uether_ifdetach()
 * (sys/bus/u4b/net/usb_ethernet.c:268-305):
 *
 *   273:   usb_proc_drain(&ue->ue_tq);        <- no-op, up_lock == NULL
 *                                               (usb_process.c:401-412)
 *   276:   ifp = uether_getifp(ue);           <- embedded arpcom ifnet
 *                                               (usb_ethernet.c:134), NEVER NULL
 *   278:   if (ifp != NULL) {                 <- dead guard, always true
 *   281:       UE_LOCK(ue);                   <- macro at usb_ethernet.c:58:
 *                                              lockmgr(NULL, LK_EXCLUSIVE)
 *                                              ==> Fatal trap 12, fault VA 0x0
 *
 * The trigger is fully device-controlled: any USB device presenting one of
 * the 6 VID:PID pairs in udav_devs[] (if_udav.c:205-218; e.g. ShanTou
 * DM9601 0x0fe6:0x8101, Corega FEther USB-TXC 0x07aa:0x0004, Kontron
 * JP1082 0x15ec/0x0fe6 variants) at config index 0 / iface 0 but WITHOUT a
 * usable bulk-IN + bulk-OUT endpoint pair makes usbd_transfer_setup() fail
 * at plug-in -> the goto fires -> panic.
 *
 * The second failure branch (uether_ifattach() error, only reachable on
 * usb_proc_create() ENOMEM) reaches udav_detach() with ue_lock valid but a
 * never-if_attach()ed ifnet: ether_ifdetach() -> if_detach() nulls
 * ifindex2ifnet[0] (stealing lo0's slot) and TAILQ_REMOVEs a never-inserted
 * ifnet from the global list — latent corruption, not attacker-timed.
 *
 * This KLD replays branch 1 verbatim: it constructs the exact post-failure
 * state (zeroed struct udav_softc + lockinit(&sc->sc_lock) per if_udav.c:260
 * — the only softc initialisation before the failing call) and calls the
 * production usbd_transfer_unsetup() and uether_ifdetach() (exported by
 * uether.ko) in udav_detach() order.  The only non-production code is the
 * static udav_attach()/udav_detach() frame itself (static in if_udav.c,
 * reachable only with a physical DM9601-class device present).
 */

#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 <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>
#include <bus/u4b/usb_process.h>

#include <bus/u4b/net/usb_ethernet.h>
#include <bus/u4b/net/if_udavreg.h>

static int
udav_poc_modevent(module_t mod, int type, void *arg)
{
	struct udav_softc *sc;

	switch (type) {
	case MOD_LOAD:
		kprintf("udav_poc: constructing post-'allocating USB transfers"
		    " failed' state (if_udav.c:260-268)\n");
		sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO);
		if (sc == NULL)
			return (ENOMEM);
		/* if_udav.c:260: the only softc field initialised before the
		 * failing usbd_transfer_setup() at if_udav.c:263 */
		lockinit(&sc->sc_lock, "udav_poc", 0, 0);
		kprintf("udav_poc: state check: ue_lock=%p ue_dev=%p "
		    "ue_udev=%p ue_methods=%p ue_tq.up_lock=%p "
		    "(all must be 0/NULL)\n",
		    sc->sc_ue.ue_lock, (void *)sc->sc_ue.ue_dev,
		    sc->sc_ue.ue_udev, sc->sc_ue.ue_methods,
		    sc->sc_ue.ue_tq.up_lock);
		kprintf("udav_poc: entering udav_detach() replay: "
		    "usbd_transfer_unsetup() + uether_ifdetach() "
		    "(if_udav.c:304-305)\n");

		/* if_udav.c:304 — production code, zeroed xfer array == branch 1 */
		usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);

		/* if_udav.c:305 -> uether_ifdetach() -> UE_LOCK(ue) ->
		 * lockmgr(NULL, LK_EXCLUSIVE) -> Fatal trap 12, VA 0x0 */
		uether_ifdetach(&sc->sc_ue);

		kprintf("udav_poc: SURVIVED uether_ifdetach() (UNEXPECTED — "
		    "guard present?)\n");
		lockuninit(&sc->sc_lock);
		kfree(sc, M_TEMP);
		return (0);
	default:
		return (0);
	}
}

static moduledata_t udav_poc_mod = { "udav_poc", udav_poc_modevent, NULL };
DECLARE_MODULE(udav_poc, udav_poc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_DEPEND(udav_poc, uether, 1, 1, 1);
