/*
 * DF-5530 — production-shape race harness for the bpfdetach() NULL-deref.
 *
 * Two kernel threads drive the two real production code paths against each
 * other on the live "usbus0" controller:
 *
 *   T1 (creator):   if_clone_create("usbus0") / if_clone_destroy("usbus0")
 *                   loop — this is exactly the SIOCIFCREATE/SIOCIFDESTROY
 *                   path (net/if.c:2005-2013, root-gated), which calls
 *                   usbpf_clone_create() under ifnet_lock.
 *
 *   T2 (detacher):  spins reading ubus->ifp with no lock — exactly what
 *                   usbpf_detach() does (usb_pf.c:209) — and calls the
 *                   production usbpf_detach(ubus) the moment it sees a
 *                   non-NULL ifp.  This is the controller-detach path
 *                   (usb_controller.c:240 -> usb_pf.c:207).
 *
 * usbpf_clone_create() publishes ubus->ifp at usb_pf.c:157 and only calls
 * bpfattach() at usb_pf.c:177.  If T2's usbpf_detach() lands inside that
 * window, usbpf_clone_destroy() (usb_pf.c:191-192) runs
 *   ubus->ifp = NULL; bpfdetach(ifp);
 * where ifp was never bpf-attach'ed -> the bpf_if search loop in
 * bpfdetach() (bpf.c:1567-1571) exits with bp == NULL and bpf.c:1574
 * dereferences bp->bif_ifp -> fatal page fault.
 *
 * Any panic whose faulting RIP sits in bpfdetach() is the finding;
 * a panic elsewhere in the create/destroy interleaving is the already
 * known DF-2347/DF-2607 family and is reported as such.
 *
 * The harness self-limits after SECS seconds and prints RACE_RESULT.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/condvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_clone.h>
#include <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>
#include <bus/u4b/usb_busdma.h>
#include <bus/u4b/usb_controller.h>
#include <bus/u4b/usb_core.h>
#include <bus/u4b/usb_process.h>
#include <bus/u4b/usb_device.h>
#include <bus/u4b/usb_bus.h>
#include <bus/u4b/usb_pf.h>
#include <bus/u4b/usb_transfer.h>

static struct thread *df5530_t1;
static struct thread *df5530_t2;
static struct thread *df5530_t3;
static struct usb_bus *df5530_ubus;
static volatile int df5530_stop;
static volatile int df5530_creates;

#define	DF5530_SECS	240

static void
df5530_creator(void *arg)
{
	char name[IFNAMSIZ];
	int i;

	strlcpy(name, "usbus0", sizeof(name));
	for (i = 0; !df5530_stop && i < 4000000; i++) {
		if (if_clone_create(name, IFNAMSIZ, NULL, NULL) == 0)
			df5530_creates++;
		if_clone_destroy(name);
		tsleep(&df5530_creates, 0, "df55c", 1);
	}
	df5530_stop = 1;
	kthread_exit();
}

static void
df5530_detacher(void *arg)
{
	struct ifnet *ifp;
	int hits = 0;

	while (!df5530_stop) {
		/*
		 * unlocked read of bus->ifp, exactly like usb_pf.c:209 in
		 * usbpf_detach().  Strike only while the create path is
		 * mid-flight: usbpf_clone_create() publishes ubus->ifp at
		 * usb_pf.c:157 and only sets IFF_UP at usb_pf.c:170, after
		 * if_attach() — and bpfattach() does not run until
		 * usb_pf.c:177.  ifp != NULL with IFF_UP clear therefore
		 * identifies the not-yet-bpf-attached window
		 * (usb_pf.c:157..177) that hands bpfdetach() an ifnet that
		 * is not in bpf_iflist.  This also keeps the harness away
		 * from the (already known, DF-2347/DF-2607) double-destroy
		 * interleaving that fires when striking a completed clone.
		 */
		ifp = df5530_ubus->ifp;
		if (ifp != NULL && (ifp->if_flags & IFF_UP) == 0) {
			hits++;
			usbpf_detach(df5530_ubus);	/* usb_pf.c:207 */
		}
	}
	kprintf("DF5530: detacher stops, hits=%d creates=%d\n",
	    hits, df5530_creates);
	kthread_exit();
}

static void
df5530_watcher(void *arg)
{
	int s;

	for (s = 0; s < DF5530_SECS; s++) {
		if (df5530_stop)
			break;
		kprintf("DF5530: racing... %ds elapsed\n", s);
		tsleep(&df5530_stop, 0, "df55w", hz);
	}
	df5530_stop = 1;
	kprintf("RACE_RESULT=NOHIT (no panic in %ds; see VERDICT.md)\n",
	    DF5530_SECS);
	kthread_exit();
}

static int
df5530_load(struct module *m, int what, void *arg)
{
	devclass_t dc;
	device_t dev;
	int error;

	switch (what) {
	case MOD_LOAD:
		dc = devclass_find("usbus");
		if (dc == NULL) {
			kprintf("DF5530: no usbus devclass\n");
			return (ENOENT);
		}
		dev = devclass_get_device(dc, 0);
		if (dev == NULL) {
			kprintf("DF5530: no usbus0 device\n");
			return (ENOENT);
		}
		df5530_ubus = device_get_softc(dev);
		if (df5530_ubus == NULL) {
			kprintf("DF5530: no softc\n");
			return (ENOENT);
		}
		kprintf("DF5530: racing if_clone_create(usbus0) vs "
		    "usbpf_detach() for up to %ds...\n", DF5530_SECS);
		df5530_stop = 0;
		error = kthread_create(df5530_watcher, NULL, &df5530_t1,
		    "df5530_watch");
		if (error)
			return (error);
		error = kthread_create(df5530_creator, NULL, &df5530_t2,
		    "df5530_creat");
		if (error)
			return (error);
		error = kthread_create(df5530_detacher, NULL, &df5530_t3,
		    "df5530_detach");
		if (error)
			return (error);
		break;
	case MOD_UNLOAD:
		df5530_stop = 1;
		break;
	default:
		break;
	}
	return (0);
}

static moduledata_t df5530_race_mod = {
	"df5530_race",
	df5530_load,
	NULL
};

DECLARE_MODULE(df5530_race, df5530_race_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df5530_race, 1);
