/*
 * DF-5493 PoC harness — BRANCH B (see ue_guard_a.c for branch A).
 *
 * Target: sys/bus/u4b/net/usb_ethernet.c uether_ifdetach() (:267-305).
 *
 * BRANCH B state = what uether_ifattach() itself leaves on its only
 * failure, usb_proc_create() ENOMEM (usb_ethernet.c:173-178):
 *
 *      ue->ue_dev/ue_udev/ue_lock/ue_methods are already set by the
 *      driver (they are checked non-NULL at :167-171 BEFORE anything
 *      happens), but usb_proc_create() failed and fully unwound ue_tq
 *      back to all-zero on its error path (usb_process.c:208-219: on
 *      USB_THREAD_CREATE failure it calls usb_proc_free() which
 *      cv_destroy()s and resets up->up_lock = NULL at :244), and
 *      ue_attach_post_task was never queued (it is queued only at
 *      :181-185 AFTER a successful usb_proc_create), so the embedded
 *      ifnet was never published via ether_ifattach().
 *
 * Every uether driver then does:
 *
 *      error = uether_ifattach(ue);
 *      if (error) { goto detach; }   -> driver_detach() ->
 *              uether_ifdetach(ue)
 *
 * (e.g. if_cue.c:418-426) and uether_ifdetach() walks straight into:
 *
 *      usb_proc_drain(&ue->ue_tq);      :273  no-op (up_lock == NULL)
 *      ifp = uether_getifp(ue);         :276  embedded, NOT NULL
 *      UE_LOCK(ue);                     :281  ue_lock IS valid -- passes
 *      ifp->if_flags &= ~IFF_RUNNING;   :282
 *      usb_callout_drain(&ue->ue_watchdog); :286  zeroed callout, passes
 *      ether_ifdetach(ifp);             :294  NEVER if_attach()ed ->
 *                                          if_down()/if_detach() on a
 *                                          zeroed ifnet -> Fatal trap
 *      sysctl_ctx_free(&ue->ue_sysctl_ctx); :297  never sysctl_ctx_init'ed
 *      devfs_clone_bitmap_put(..., ue->ue_unit); :300 ue_unit==0 never
 *                                          allocated (would corrupt the
 *                                          unit bitmap for a live ue0)
 *
 * This KLD replays the exact state + the single production call
 * uether_ifdetach() from the stock uether.ko.
 */

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

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

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

static struct lock poc_lk;
static struct usb_ether *poc_ue;

static int
ue_guard_b_modevent(module_t mod, int type, void *arg)
{
	switch (type) {
	case MOD_LOAD:
		poc_ue = kmalloc(sizeof(*poc_ue), M_TEMP, M_WAITOK | M_ZERO);
		if (poc_ue == NULL)
			return (ENOMEM);
		/* Driver set ue_lock before uether_ifattach()
		 * (usb_ethernet.c:169 requires it non-NULL);
		 * usb_proc_create() then failed and unwound ue_tq
		 * to all-zero (usb_process.c:217-219,244). */
		lockinit(&poc_lk, "uegb", 0, 0);
		poc_ue->ue_lock = &poc_lk;
		kprintf("ue_guard_b: branch B state: ue_lock valid, ue_tq "
		    "zeroed (uether_ifattach ENOMEM at "
		    "usb_ethernet.c:173-178)\n");
		kprintf("ue_guard_b: ue=%p ue_lock=%p ue_tq.up_lock=%p "
		    "(up_lock must be NULL)\n", poc_ue, poc_ue->ue_lock,
		    poc_ue->ue_tq.up_lock);
		kprintf("ue_guard_b: calling production uether_ifdetach() "
		    "-> ether_ifdetach() on never-if_attach()ed ifnet\n");
		uether_ifdetach(poc_ue);
		kprintf("ue_guard_b: SURVIVED uether_ifdetach() (UNEXPECTED "
		    "-- guard present?)\n");
		lockuninit(&poc_lk);
		kfree(poc_ue, M_TEMP);
		return (0);
	default:
		return (0);
	}
}

static moduledata_t ue_guard_b_mod = { "ue_guard_b", ue_guard_b_modevent, NULL };
DECLARE_MODULE(ue_guard_b, ue_guard_b_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_DEPEND(ue_guard_b, uether, 1, 1, 1);
