/*
 * DF-3784 -- vbi_read() 64-bit iov_len truncated to int -> negative readsize
 *           -> uiomove(vbibuffer+start, (size_t)INT_MIN) -> mass kernel leak
 *
 * Source-level harness porting the exact decision logic of
 * sys/dev/video/bktr/bktr_core.c:1124-1194 (vbi_read) and the length
 * arithmetic of sys/kern/kern_subr.c uiomove().
 */
#include <stdio.h>
#include <sys/types.h>

#define VBI_BUFFER_SIZE	(2048 * 16 * 2 * 20)	/* bktr_core.c:174 */

int main(void)
{
	int readsize, readsize2, start;
	int vbistart = 0, vbisize = 65536;	/* one decoded VBI frame present */
	size_t iov_len = 0x80000000UL;		/* read(fd, buf, 2GB): legal */
	size_t n;				/* uiomove's size_t parameter */

	printf("iov_len (read() 3rd arg) = 0x%zx  (valid: (ssize_t)>0)\n",
	       iov_len);

	/* ---- bktr_core.c:1159-1161 ---- */
	readsize = (int)iov_len;
	printf("vbi_read: readsize = (int)iov_len = %d\n", readsize);
	if (readsize > vbisize)
		readsize = vbisize;
	printf("clamp 'if (readsize > vbisize)' -> readsize still %d "
	       "(negative, clamp skipped)\n", readsize);

	/* ---- bktr_core.c:1165-1178 ---- */
	if ((vbistart + readsize) >= VBI_BUFFER_SIZE) {
		printf("wrap branch taken (unexpected)\n");
		return 1;
	}
	printf("wrap check '(vbistart+readsize) >= VBI_BUFFER_SIZE': %d >= %d "
	       "-> false, straight-through branch\n",
	       vbistart + readsize, VBI_BUFFER_SIZE);

	/* ---- bktr_core.c:1177: uiomove(vbibuffer + vbistart, readsize, uio)
	 *      kern_subr.c uiomove(caddr_t, size_t n, ...): int -> size_t ---- */
	n = (size_t)readsize;
	printf("uiomove n = (size_t)(int)0x80000000 = 0x%zx (%zu)\n", n, n);

	/* ---- kern_subr.c uiomove loop: cnt = MIN(iov_len, n) ---- */
	{
		size_t cnt = iov_len;
		if (cnt > n) cnt = n;
		printf("uiomove cnt = MIN(iov_len=2GB, n) = %zu bytes "
		       "copied from vbibuffer+0 to user buffer\n", cnt);
		printf("=> 2 GiB of kernel virtual memory disclosed from "
		       "vbibuffer upward until first unmapped page (EFAULT "
		       "stops it; partial data already in user buffer)\n");
	}

	/* ---- state corruption: bktr_core.c:1183-1187 ---- */
	vbisize -= readsize;
	vbistart += readsize;
	printf("post-state: vbisize=%d vbistart=%d (both corrupted by int "
	       "overflow)\n", vbisize, vbistart);

	printf("CONFIRMED: no guard rejects a negative readsize in vbi_read\n");
	return 0;
}
