/*
 * DF-1046 userspace harness — replicates the EXACT loop semantics of
 * sys/bus/u4b/uvc/uvc_ctrls.c:917-978 (uvc_ctrl_init_dev) and proves the
 * uint8_t-loop-counter wrap is an unbounded-write primitive.
 *
 *   cc -DBUGGY -O2 -o harness_buggy harness.c   # current kernel: uint8_t i
 *   cc            -O2 -o harness_fixed harness.c   # proposed fix: unsigned int i
 *
 * The "buggy" build uses uint8_t for `i` (as the kernel does today at line 925);
 * the "fixed" build uses unsigned int (the proposed fix). The buggy build will
 * (a) loop ~forever, and (b) write far past the nctrls-sized array.
 *
 * ITER_CAP is enforced so the harness finishes; the kernel has no such cap.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define UVC_BITSHIFT  3
#define UVC_BITMASK   0x7
#define UVC_VALMASK   0x1

static int uvc_test_bit(const uint8_t *buf, int b) {
	return (buf[b >> UVC_BITSHIFT] >> (b & UVC_BITMASK)) & UVC_VALMASK;
}

/* Mirror of uvc_ctrl_count_control — uses `int i` (the CORRECT width). */
static unsigned int count_control(const uint8_t *bm, unsigned int bs) {
	int i;
	unsigned int n = 0;
	for (i = 0; i < (int)(bs * 8); i++)
		if ((bm[i >> UVC_BITSHIFT] >> (i & UVC_BITMASK)) & UVC_VALMASK)
			n++;
	return n;
}

#define ITER_CAP 2000000   /* kill-switch; the kernel has none */
#define OOB_CAP  4096      /* bail after this many OOB writes (proof enough) */
#define SLOT     64        /* stand-in sizeof(struct uvc_control) */

int main(int argc, char **argv) {
	unsigned int bCtrlSize = (argc > 1) ? (unsigned)strtoul(argv[1], NULL, 0) : 32;
	unsigned int nctrls;
	uint8_t *bm, *sink;
	unsigned int sink_bytes;
	unsigned long writes_in = 0, writes_oob = 0, iters = 0;
	int oob_first = -1;

	if (bCtrlSize == 0) { fprintf(stderr, "bCtrlSize=0: nothing to do\n"); return 0; }

	/* bmControls: bCtrlSize bytes, only bit 0 set (Brightness) */
	bm = calloc(bCtrlSize, 1);
	if (!bm) { perror("calloc bm"); return 2; }
	bm[0] = 0x01;

	nctrls = count_control(bm, bCtrlSize);
	printf("bCtrlSize=%u  bCtrlSize*8=%u  nctrls(set bits)=%u\n",
	       bCtrlSize, bCtrlSize * 8u, nctrls);

	/* Mirror of kmalloc(nctrls * sizeof(struct uvc_control)). Generous
	 * padding (16 MB) so OOB writes don't immediately segfault — we want
	 * to COUNT them, not just crash. The kernel has no such luxury: the
	 * OOB writes corrupt adjacent slab objects until something page-faults. */
	sink_bytes = nctrls * (unsigned)SLOT;
	sink = calloc(sink_bytes + (16u << 20), 1);
	if (!sink) { perror("calloc sink"); return 2; }
	memset(sink + sink_bytes, 0xAA, (16u << 20));

	/* The init loop — structurally identical to uvc_ctrls.c:963-974.
	 * IMPORTANT: do NOT cast the loop bound. In the kernel both operands
	 * promote to int, so the bound stays at bCtrlSize*8 (e.g. 256) while
	 * only `i` is narrow. That asymmetry IS the bug. */
	{
		uint8_t *ctrl = sink;                       /* topo_node->controls */
#ifdef BUGGY
		uint8_t i;                                  /* THE BUG (uvc_ctrls.c:925) */
#else
		unsigned int i;                             /* the fix */
#endif
		for (i = 0; i < bCtrlSize * 8u; i++) {
			if (uvc_test_bit(bm, i) == 0)
				continue;
			/* uvc_ctrl_initialize_control(ctrl) would kmalloc uvc_data etc.
			 * here. We just stamp the slot to detect OOB. */
			if ((uintptr_t)(ctrl - sink) < sink_bytes)
				writes_in++;
			else {
				if (oob_first < 0) oob_first = (int)iters;
				writes_oob++;
			}
			*ctrl = 0xCC;
			ctrl += SLOT;                           /* ctrl++ */
			iters++;
			if (iters >= ITER_CAP) break;
			if (writes_oob >= OOB_CAP) { iters |= 0x80000000UL; break; }
		}
	}

	printf("iterations executed: %lu (cap=%d)\n", iters, ITER_CAP);
	printf("writes IN-BOUNDS   : %lu (expected nctrls=%u)\n", writes_in, nctrls);
	printf("writes OUT-OF-BOUNDS: %lu%s\n", writes_oob,
	       writes_oob ? "  <-- UNBOUNDED HEAP OVERFLOW (primitive confirmed)" : "");
	if (oob_first >= 0)
		printf("first OOB write at iter %d (i had wrapped at least once)\n", oob_first);

#ifdef BUGGY
	if (bCtrlSize >= 32 && (writes_oob > 0 || iters >= ITER_CAP)) {
		printf("\nVERDICT: BUGGY — uint8_t i wraps when bCtrlSize*8 > 255;\n"
		       "        the loop fails to terminate and writes struct uvc_control\n"
		       "        entries (with pointers / nested kmallocs) past the allocation.\n");
		return 0;
	}
#else
	if (writes_oob == 0 && writes_in == nctrls) {
		printf("\nVERDICT: FIXED — unsigned int i holds the full range; loop\n"
		       "        terminates after exactly nctrls in-bounds writes.\n");
		return 0;
	}
#endif
	printf("\nVERDICT: unexpected — investigate.\n");
	return 1;
}
