/*
 * DF-2208 / DF-2209 verbatim-code rig - HEAD
 *
 * Compiles the UNMODIFIED production functions
 *   drm_sysfs_connector_add    (sys/dev/drm/drm_sysfs.c:31-46)
 *   drm_sysfs_connector_remove (sys/dev/drm/drm_sysfs.c:48-54)
 * kernel-resident and drives them with fabricated drm_connector state.
 *
 * Everything below the "production-faithful shims" banner mirrors the
 * shipped definitions byte-for-byte in SHAPE (field order, macro
 * semantics, flag values); the ONLY deltas are:
 *   - allocation faults can be injected (rig_fail_kzalloc /
 *     rig_fail_kasprintf) at exactly the call sites the findings cite,
 *     modelling the Linux-GFP_KERNEL OOM window that the finding claims
 *     (on DragonFly GFP_KERNEL == M_WAITOK|M_CACHEALIGN, and M_WAITOK
 *     allocations PANIC or BLOCK instead of returning NULL -- see
 *     sys/kern/kern_slaballoc.c:869-878 limit path, :1722-1724
 *     kernel_map path, :1786-1800 page-starvation retry loop).
 *   - the pool is M_DF22 (own malloc_type) instead of M_DRM so the rig
 *     is self-contained (no drm.ko dependency); zone/slab semantics of
 *     kmalloc/kfree are the real allocator.
 *   - DRM_DEBUG prints unconditionally (content capture).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/stdarg.h>

MALLOC_DEFINE(M_DF22, "df22pool", "DF2208/DF2209 rig pool (mirrors M_DRM)");

/* ------------------------------------------------------------------ */
/* Production-faithful type shims                                      */
/*   struct kobject ......... include/linux/kobject.h:50-54 (order)    */
/*   struct device .......... include/linux/device.h:44-54 (order)     */
/*   offsetof(struct device, kobj.name) == 8 in production AND here    */
/* ------------------------------------------------------------------ */
struct kref { int refcount; };			/* linux/kref.h */
struct kobj_type {
	void		(*release)(void *);
	const void	*sysfs_ops;
	void		**default_attrs;
};
struct kobject {
	const char	*name;			/* kobject.h:51 */
	struct kref	kref;			/* kobject.h:52 */
	struct kobj_type *ktype;			/* kobject.h:53 */
};
struct dev_pm_info { int rig_pad; };		/* stand-in, past kobj */
struct device {
	struct device	*parent;			/* device.h:45 */
	struct kobject	kobj;				/* device.h:47 */
	void		*bsddev;				/* device.h:49 (device_t) */
	void		*driver_data;			/* device.h:51 */
	struct dev_pm_info power;				/* device.h:53 */
};

struct drm_minor { int index; };
struct drm_device { struct drm_minor *primary; };
struct drm_connector {
	struct device		*kdev;		/* drm_connector.h:833 */
	struct drm_device	*dev;
	char			name[32];	/* drm_connector.h (name[]) */
};

/* linux/gfp.h:16 verbatim */
#define GFP_KERNEL	(M_WAITOK | M_CACHEALIGN)

/* drm_print.h:388 -- normally debug-flag gated; here: capture */
#define DRM_DEBUG(fmt, ...)						\
	kprintf("DF22   [drm_sysfs] " fmt, ##__VA_ARGS__)

/* ------------------------------------------------------------------ */
/* Fault-injecting allocator shims                                     */
/*   kzalloc  == include/linux/slab.h:35  kmalloc(size, M_DRM, f|M_ZERO)*/
/*   kfree    == include/linux/slab.h:38-42 _kfree(ptr, M_DRM) guarded  */
/*   kasprintf== drm_dragonfly.c:87-94 -> kvasprintf drm_dragonfly.c:65 */
/* ------------------------------------------------------------------ */
static int rig_fail_kzalloc;		/* inject NULL at drm_sysfs.c:39 */
static int rig_fail_kasprintf;		/* inject NULL at drm_sysfs.c:40 */
static unsigned long rig_kzalloc_calls, rig_kfree_calls, rig_kasprintf_calls;
static unsigned long rig_alloc_events, rig_free_events;

#undef kzalloc
#define kzalloc(size, flags)	rig_kzalloc((size), (flags))
static void *
rig_kzalloc(size_t size, int flags)
{
	void *p;

	rig_kzalloc_calls++;
	if (rig_fail_kzalloc)
		return (NULL);		/* INJECTION (see file header) */
	p = kmalloc(size, M_DF22, flags | M_ZERO);	/* slab.h:35 shape */
	if (p != NULL)
		rig_alloc_events++;
	return (p);
}

#undef kfree
#define kfree(ptr) do {						\
	if ((ptr) != NULL) {					\
		rig_kfree_calls++;				\
		rig_free_events++;				\
		_kfree((void *)(ptr), M_DF22);			\
	}							\
} while (0)

/* drm_dragonfly.c:65-82 verbatim shape, M_DF22 pool */
static char *
rig_vasprintf(int flags, const char *format, va_list ap)
{
	char *str;
	size_t size;
	va_list aq;

	va_copy(aq, ap);
	size = kvsnprintf(NULL, 0, format, aq);
	va_end(aq);

	str = kmalloc(size + 1, M_DF22, flags);
	if (str == NULL)
		return (NULL);		/* drm_dragonfly.c:78-79 */
	rig_alloc_events++;

	kvsnprintf(str, size + 1, format, ap);
	return (str);
}

#undef kasprintf
#define kasprintf(flags, format, ...)	\
	rig_kasprintf((flags), (format), ##__VA_ARGS__)
static char *
rig_kasprintf(int flags, const char *format, ...)
{
	va_list ap;
	char *str;

	rig_kasprintf_calls++;
	if (rig_fail_kasprintf)
		return (NULL);		/* INJECTION (see file header) */
	va_start(ap, format);
	str = rig_vasprintf(flags, format, ap);
	va_end(ap);
	return (str);
}

/* verbatim (baseline) + fix.diff variants, extracted by build.sh */
int	drm_sysfs_connector_add(struct drm_connector *connector);
void	drm_sysfs_connector_remove(struct drm_connector *connector);
int	drm_sysfs_connector_add_fixed(struct drm_connector *connector);
void	drm_sysfs_connector_remove_fixed(struct drm_connector *connector);
int drm_sysfs_connector_add(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;

	if (connector->kdev)
		return 0;

	/* Linux uses device_create_with_groups() here */
	connector->kdev = kzalloc(sizeof(struct device), GFP_KERNEL);
	connector->kdev->kobj.name = kasprintf(GFP_KERNEL, "card%d-%s",
					       dev->primary->index,
					       connector->name);
	DRM_DEBUG("adding \"%s\" to sysfs\n", connector->name);

	return 0;
}
void drm_sysfs_connector_remove(struct drm_connector *connector)
{
	DRM_DEBUG("removing \"%s\" from sysfs\n", connector->name);

	if (connector->kdev)
		kfree(connector->kdev);
}
int drm_sysfs_connector_add_fixed(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;

	if (connector->kdev)
		return 0;

	/* Linux uses device_create_with_groups() here */
	connector->kdev = kzalloc(sizeof(struct device), GFP_KERNEL);
	if (!connector->kdev)
		return -ENOMEM;
	connector->kdev->kobj.name = kasprintf(GFP_KERNEL, "card%d-%s",
					       dev->primary->index,
					       connector->name);
	if (!connector->kdev->kobj.name) {
		kfree(connector->kdev);
		connector->kdev = NULL;
		return -ENOMEM;
	}
	DRM_DEBUG("adding \"%s\" to sysfs\n", connector->name);

	return 0;
}
void drm_sysfs_connector_remove_fixed(struct drm_connector *connector)
{
	DRM_DEBUG("removing \"%s\" from sysfs\n", connector->name);

	if (connector->kdev) {
		kfree(connector->kdev->kobj.name);
		kfree(connector->kdev);
		connector->kdev = NULL;
	}
}
/*
 * DF-2208 / DF-2209 verbatim-code rig - TAIL
 * Matrix driver + module glue. Runs the UNMODIFIED production functions
 * (concatenated above, extracted from tree lines 31-46 / 48-54) and the
 * fix.diff variants over the exact scenarios that flip each finding:
 *
 * DF-2208 (missing NULL checks in drm_sysfs_connector_add):
 *   A  control      - normal add/remove, name content capture
 *   B  kasprintf-NU - add with injected kasprintf==NULL:
 *                     ret==0 (SUCCESS!) and kdev->kobj.name==NULL planted
 *   C  kzalloc-NU   - add with injected kzalloc==NULL: write through
 *                     NULL->kobj.name at offset 8 -> FATAL PAGE FAULT.
 *                     Armed via sysctl kern.df2208_fire=1 (panics; the
 *                     guest dies, expected). Fixed variant: ret=-ENOMEM.
 *
 * DF-2209 (stale pointer after kfree in drm_sysfs_connector_remove):
 *   D  UAF cycle    - add -> remove (kdev NOT NULLed: drm_sysfs.c:52-53)
 *                     -> reclaim the freed slot, plant forged content
 *                     -> read kdev->kobj.name (the exact production
 *                        consumer expression, intel_dp.c:5251) through
 *                        the DANGLING pointer -> attacker-controlled data
 *                     -> re-add: ret==0 with ZERO new allocations
 *                        (guard drm_sysfs.c:35-36 turns add into a silent
 *                        no-op while kdev dangles)
 *                     -> second remove: kfree()s the RECLAIMED foreign
 *                        object (free-of-live-object primitive)
 *   F  fixed cycle  - same sequence through the fix.diff variants:
 *                     remove NULLs kdev; re-add allocates FRESH.
 */
static struct drm_minor		g_minor = { .index = 0 };
static struct drm_device		g_drmdev = { .primary = &g_minor };

static void
rig_conn_init(struct drm_connector *c, const char *nm)
{
	memset(c, 0, sizeof(*c));
	c->dev = &g_drmdev;
	ksnprintf(c->name, sizeof(c->name), "%s", nm);
}

/* registry so kldunload is clean even though production remove() leaks
 * the kasprintf'd name string (side observation: allocs never balanced) */
#define RIG_REG_MAX 16
static void	*rig_reg[RIG_REG_MAX];
static int	rig_reg_n;
static void
rig_reg_add(void *p)
{
	if (p != NULL && rig_reg_n < RIG_REG_MAX)
		rig_reg[rig_reg_n++] = p;
}

/* ------------------------------------------------------------------ */
/* DF-2208 CASE A: control                                             */
/* ------------------------------------------------------------------ */
static void
case_A_control(void)
{
	struct drm_connector c;
	const char *nm;
	int ret;
	unsigned long a0 = rig_alloc_events, f0 = rig_free_events;

	rig_conn_init(&c, "DP-1");
	rig_fail_kzalloc = 0;
	rig_fail_kasprintf = 0;

	kprintf("DF2208 CASE A (control): verbatim add/remove\n");
	ret = drm_sysfs_connector_add(&c);
	nm = (c.kdev != NULL) ? c.kdev->kobj.name : NULL;
	rig_reg_add((void *)nm);
	kprintf("DF2208   ret=%d kdev=%p name='%s' (content capture: "
		"card%%d-%%s of dev->primary->index=%d, connector->name='%s')\n",
		ret, c.kdev, nm ? nm : "(null)", g_minor.index, c.name);
	kprintf("DF2208   offsetof(struct device, kobj.name)=%lu "
		"sizeof(struct device)=%lu (M_CACHEALIGN -> power-of-2 class)\n",
		(unsigned long)__offsetof(struct device, kobj.name),
		(unsigned long)sizeof(struct device));

	drm_sysfs_connector_remove(&c);
	kprintf("DF2208   after remove: connector->kdev still reads %p "
		"(baseline remove NULLs nothing); alloc_events=%lu "
		"free_events=%lu (delta alloc=%lu free=%lu)\n",
		c.kdev, rig_alloc_events - a0, rig_free_events - f0,
		rig_alloc_events - a0, rig_free_events - f0);
	if (rig_alloc_events - a0 == rig_free_events - f0 + 1)
		kprintf("DF2208   SIDE-OBSERVATION: the kasprintf'd "
			"kobj.name string is never freed by remove() "
			"(drm_sysfs.c:52-53 frees kdev only) -> leaks 1 "
			"allocation per connector add/remove cycle\n");
}

/* ------------------------------------------------------------------ */
/* DF-2208 CASE B: injected kasprintf failure                           */
/* ------------------------------------------------------------------ */
static void
case_B_kasprintf_null(void)
{
	struct drm_connector c;
	int ret;

	rig_conn_init(&c, "DP-2");
	rig_fail_kzalloc = 0;
	rig_fail_kasprintf = 1;		/* INJECTION */

	kprintf("DF2208 CASE B (kasprintf==NULL injected at drm_sysfs.c:40):\n");
	ret = drm_sysfs_connector_add(&c);
	kprintf("DF2208   ret=%d kdev=%p kobj.name=%p\n",
		ret, c.kdev, c.kdev ? c.kdev->kobj.name : NULL);
	if (ret == 0 && c.kdev != NULL && c.kdev->kobj.name == NULL) {
		kprintf("DF2208   VERDICT: SILENT NULL PLANTED -- function "
			"reports SUCCESS (ret==0) with kdev->kobj.name==NULL; "
			"first consumer deref (intel_dp.c:5251 "
			"connector->kdev->kobj.name) hits NULL\n");
	}
	rig_fail_kasprintf = 0;
	drm_sysfs_connector_remove(&c);	/* kdev freed; name NULL: guarded */
}

/* ------------------------------------------------------------------ */
/* DF-2208 CASE C: injected kzalloc failure (BASELINE = PANIC)          */
/* Armed via sysctl kern.df2208_fire=1 (fires from sysctl context).     */
/* ------------------------------------------------------------------ */
static struct drm_connector g_fire_conn;

static void
rig_fire_df2208(void)
{
	rig_conn_init(&g_fire_conn, "DP-FIRE");
	rig_fail_kzalloc = 1;		/* INJECTION at drm_sysfs.c:39 */
	kprintf("DF2208 CASE C FIRING: kzalloc==NULL injected; verbatim "
		"drm_sysfs_connector_add about to write "
		"connector->kdev->kobj.name at NULL+8 -- EXPECT FATAL TRAP\n");
	(void)drm_sysfs_connector_add(&g_fire_conn);
	kprintf("DF2208   UNEXPECTED: returned without fault\n");
	rig_fail_kzalloc = 0;
}

static int	df2208_fire_val;
static int	df2208_fired;

static int
df2208_fire_sysctl(SYSCTL_HANDLER_ARGS)
{
	int error, val = df2208_fire_val;

	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);
	if (val == 1 && !df2208_fired) {
		df2208_fired = 1;
		rig_fire_df2208();
	}
	df2208_fire_val = val;
	return (0);
}
SYSCTL_PROC(_kern, OID_AUTO, df2208_fire, CTLTYPE_INT | CTLFLAG_RW,
    0, 0, df2208_fire_sysctl, "I",
    "write 1: fire DF-2208 kzalloc-NULL case through verbatim add (PANICS)");

/* ------------------------------------------------------------------ */
/* DF-2209 CASE D: UAF cycle through verbatim add/remove                */
/* ------------------------------------------------------------------ */
#define RECLAIM_POISON	0x41

static void
case_D_uaf_cycle(void)
{
	struct drm_connector c;
	struct device *A, *R = NULL, *tmp;
	const char *planted = "DF2209-PWNED-BY-RECLAIM";
	char *planted_str;
	const char *owner_name;
	unsigned long kz0;
	void *cands[8];
	int ncand = 0, i;

	rig_conn_init(&c, "DP-3");
	rig_fail_kzalloc = 0;
	rig_fail_kasprintf = 0;

	kprintf("DF2209 CASE D (UAF cycle): verbatim add -> remove -> "
		"reclaim -> consume -> re-add -> remove\n");

	/* step 1: add -- kdev allocated */
	if (drm_sysfs_connector_add(&c) != 0 || c.kdev == NULL) {
		kprintf("DF2209   setup failed\n");
		return;
	}
	A = c.kdev;
	rig_reg_add((void *)A->kobj.name);	/* hygiene registry */
	kprintf("DF2209   [1] add: kdev=%p (kobj.name='%s')\n",
		A, A->kobj.name);

	/* step 2: remove -- kfree(kdev) but kdev NOT NULLed (lines 52-53) */
	drm_sysfs_connector_remove(&c);
	kprintf("DF2209   [2] remove: kfree(%p) done; connector->kdev still "
		"== %p (DANGLING -- drm_sysfs.c:52-53 never NULLs it)\n",
		A, c.kdev);

	/* step 3: reclaim the freed slot from the same zone/size class.
	 * Same geometry as production kzalloc: GFP_KERNEL|M_ZERO rounds
	 * sizeof(struct device) up to the same power-of-2 kmalloc class.
	 * planted_str comes from the 128-byte class so it cannot steal
	 * the 64-class slot being reclaimed. */
	planted_str = kmalloc(128, M_DF22, M_WAITOK | M_ZERO);
	ksnprintf(planted_str, 128, "%s", planted);
	rig_reg_add(planted_str);
	for (i = 0; i < 8 && R == NULL; i++) {
		tmp = kmalloc(sizeof(struct device), M_DF22,
		    M_WAITOK | M_CACHEALIGN | M_ZERO);
		rig_alloc_events++;
		if ((void *)tmp == (void *)A) {
			R = tmp;
		} else {
			cands[ncand++] = tmp;
		}
	}
	if (R != NULL) {
		memset(R, RECLAIM_POISON, sizeof(*R));
		R->kobj.name = planted_str;		/* forged content */
		kprintf("DF2209   [3] reclaim: fresh kmalloc(%lu, M_DF22, "
			"M_WAITOK|M_CACHEALIGN|M_ZERO) returned %p == freed A "
			"(IMMEDIATE SAME-ZONE REUSE); block poisoned 0x%02x "
			"and kobj.name forged\n",
			(unsigned long)sizeof(struct device), R,
			RECLAIM_POISON);

		/* step 4: the production consumer expression
		 * (intel_dp.c:5251) reads through the DANGLING pointer */
		kprintf("DF2209   [4] consumer read connector->kdev->kobj."
			"name -> '%s' (UAF READ returns ATTACKER-CONTROLLED "
			"data; was 'card0-DP-3' before the free)\n",
			c.kdev->kobj.name);
	} else {
		kprintf("DF2209   [3] no immediate reuse in 8 tries "
			"(dangling pointer + silent re-add still proven "
			"below); freeing %d stray candidates\n", ncand);
		for (i = 0; i < ncand; i++)
			kfree(cands[i]);
	}

	/* step 5: re-add -- guard sees the dangling pointer, returns 0
	 * with ZERO allocations (drm_sysfs.c:35-36). Runs regardless of
	 * reclaim success: needs only the dangling non-NULL kdev. */
	kz0 = rig_kzalloc_calls;
	if (drm_sysfs_connector_add(&c) == 0 &&
	    rig_kzalloc_calls == kz0) {
		kprintf("DF2209   [5] re-add: ret==0 (SUCCESS) with "
			"rig_kzalloc_calls UNCHANGED (%lu) -- guard at "
			"drm_sysfs.c:35-36 turned add into a silent no-op "
			"on the DANGLING pointer\n", kz0);
	} else {
		kprintf("DF2209   [5] re-add: UNEXPECTED (allocated or "
			"errored)\n");
	}

	/* step 6: second remove frees the RECLAIMED (foreign, live) object
	 * -- free-of-live-object primitive. Only executed when the slot
	 * was reclaimed (R == A): then this kfree is a LEGITIMATE single
	 * free of R from the allocator view, but R belongs to another
	 * owner -- exactly the cross-object free the bug grants. */
	if (R != NULL) {
		drm_sysfs_connector_remove(&c);
		kprintf("DF2209   [6] second remove: production kfree()d the "
			"RECLAIMED live object %p owned by another allocator "
			"(CROSS-OBJECT FREE); owner still holds the pointer\n",
			R);
		/* owner-after-free read: pointer VALUE only (the block is
		 * freed; do not deref unknown contents) */
		owner_name = R->kobj.name;
		kprintf("DF2209   [6] owner-after-free read R->kobj.name -> "
			"%p%s\n", owner_name,
			owner_name == planted_str ?
			" (== forged string, intact)" :
			" (clobbered by allocator free-list)");
		kprintf("DF2209   VERDICT: UAF CONFIRMED through verbatim "
			"drm_sysfs.c code (dangling ptr + silent re-add + "
			"controlled reclaim + cross-object free)\n");
		/* NOTE: R was freed by production remove(); must NOT be
		 * freed again here (would be a rig double-free). */
	} else {
		kprintf("DF2209   VERDICT (partial): dangling ptr + silent "
			"re-add proven; reclaim aliasing not observed this "
			"run\n");
	}
}

/* ------------------------------------------------------------------ */
/* FIXED variants (fix.diff applied, renamed _fixed)                    */
/* ------------------------------------------------------------------ */
static void
case_E_fixed_2208(void)
{
	struct drm_connector c;
	int ret;

	kprintf("DF2208 CASE E (fix.diff variants, same injections):\n");

	rig_conn_init(&c, "DP-4");
	rig_fail_kzalloc = 1;
	rig_fail_kasprintf = 0;
	ret = drm_sysfs_connector_add_fixed(&c);
	kprintf("DF2208   [kzalloc-NULL] ret=%d (%s) kdev=%p -- NO fault, "
		"caller (drm_connector.c:445-446) can fail loudly\n",
		ret, ret == -ENOMEM ? "-ENOMEM" : "?", c.kdev);

	rig_conn_init(&c, "DP-4");
	rig_fail_kzalloc = 0;
	rig_fail_kasprintf = 1;
	ret = drm_sysfs_connector_add_fixed(&c);
	kprintf("DF2208   [kasprintf-NULL] ret=%d kdev=%p kobj.name=%p -- "
		"kdev freed and NULLed, NO silent NULL planted\n",
		ret, c.kdev, c.kdev ? c.kdev->kobj.name : NULL);
	rig_fail_kasprintf = 0;
}

static void
case_F_fixed_2209(void)
{
	struct drm_connector c;
	struct device *first, *before;
	unsigned long kz0;

	rig_conn_init(&c, "DP-5");
	rig_fail_kzalloc = 0;
	rig_fail_kasprintf = 0;

	kprintf("DF2209 CASE F (fix.diff variants):\n");
	if (drm_sysfs_connector_add_fixed(&c) != 0 || c.kdev == NULL) {
		kprintf("DF2209   setup failed\n");
		return;
	}
	first = c.kdev;
	/* NOTE: no rig_reg_add here -- the FIXED remove() frees the name
	 * string itself (fix.diff adds kfree(kobj.name)); registry would
	 * double-free at unload. */

	drm_sysfs_connector_remove_fixed(&c);
	kprintf("DF2209   [1] fixed remove: kdev=%p (NULLed: fix breaks the "
		"dangling pointer)\n", c.kdev);

	/* occupy the just-freed slot so the fresh alloc is provably new */
	before = kmalloc(sizeof(struct device), M_DF22,
	    M_WAITOK | M_CACHEALIGN | M_ZERO);
	rig_alloc_events++;

	kz0 = rig_kzalloc_calls;
	if (drm_sysfs_connector_add_fixed(&c) == 0 &&
	    rig_kzalloc_calls == kz0 + 1 && c.kdev != NULL &&
	    (void *)c.kdev != (void *)before && c.kdev != first) {
		kprintf("DF2209   [2] fixed re-add: FRESH allocation %p != "
			"freed %p (and != interposed %p), kzalloc_calls "
			"%lu -> %lu, name='%s' -- no dangling, no silent "
			"no-op\n",
			c.kdev, first, before, kz0, rig_kzalloc_calls,
			c.kdev->kobj.name);
		drm_sysfs_connector_remove_fixed(&c);
		kfree(before);
		kprintf("DF2209   VERDICT: fixed cycle clean\n");
	} else {
		kprintf("DF2209   [2] fixed re-add: UNEXPECTED (kz0=%lu "
			"now=%lu kdev=%p)\n",
			kz0, rig_kzalloc_calls, c.kdev);
	}
}

/* ------------------------------------------------------------------ */
/* module glue                                                          */
/* ------------------------------------------------------------------ */
static void
rig_matrix(void)
{
	kprintf("DF22 RIG start: kernel-resident verbatim drm_sysfs.c "
		"add/remove (baseline) + fix.diff variants\n");
	kprintf("DF22   shims: kzalloc/kfree/kasprintf mirror slab.h:35,38 "
		"and drm_dragonfly.c:65-94 with fault injection ONLY at the "
		"cited call sites\n");
	case_A_control();
	case_B_kasprintf_null();
	case_D_uaf_cycle();
	case_E_fixed_2208();
	case_F_fixed_2209();
	kprintf("DF22 RIG done (baseline kzalloc-NULL panic case ARMED: "
		"sysctl kern.df2208_fire=1)\n");
}

static int
df22_modevent(module_t mod, int type, void *arg)
{
	switch (type) {
	case MOD_LOAD:
		rig_matrix();
		return (0);
	case MOD_UNLOAD: {
		int i;
		for (i = 0; i < rig_reg_n; i++)
			kfree(rig_reg[i]);
		return (0);
	}
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df22_mod = {
	"df22_rig",
	df22_modevent,
	NULL
};

DECLARE_MODULE(df22_rig, df22_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df22_rig, 1);
