⬢ DragonFlyBSD Kernel Audit
DF-5428 / xu_trig.c
← back to finding ↓ download raw
/*
 * xu_trig.c - unprivileged trigger for DF-5428:
 * UVCIOC_CTRL_QUERY (uvc_v4l2.c:677-684) does copyin() on the KERNEL ioctl
 * buffer that mapped_ioctl() already staged (sys/kern/sys_generic.c:674-697),
 * so the ioctl can never succeed - it returns EFAULT unconditionally, even
 * with a perfectly valid user buffer.  The whole XU-control-query feature is
 * dead code from userland.
 *
 * Expected output (bug manifest): BOTH requests fail with errno=EFAULT
 * (14) even though q.data points to perfectly valid user memory.
 */
#include <sys/types.h>
#include <sys/ioccom.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

struct uvc_xu_control_query {
	uint8_t unit;
	uint8_t selector;
	uint8_t query;
	uint16_t size;
	uint8_t *data;
};
#define UVCIOC_CTRL_QUERY _IOWR('u', 0x21, struct uvc_xu_control_query)

int
main(void)
{
	struct uvc_xu_control_query q;
	uint8_t buf[64];
	int fd, ret;

	fd = open("/dev/video97", O_RDWR);
	if (fd < 0) {
		perror("open /dev/video97");
		return (1);
	}
	printf("XUTRIG: opened /dev/video97 uid=%d\n", getuid());

	memset(&q, 0, sizeof(q));
	memset(buf, 0xAB, sizeof(buf));
	q.unit = 2;
	q.selector = 1;
	q.query = 0x81;		/* GET_CUR */
	q.size = 4;
	q.data = buf;		/* valid user pointer */
	ret = ioctl(fd, UVCIOC_CTRL_QUERY, &q);
	printf("XUTRIG GET_CUR : ret=%d errno=%d (%s)  buf[0..3]=%02x%02x%02x%02x\n",
	    ret, errno, strerror(errno), buf[0], buf[1], buf[2], buf[3]);

	memset(&q, 0, sizeof(q));
	q.unit = 2;
	q.selector = 1;
	q.query = 0x01;		/* SET_CUR */
	q.size = 4;
	q.data = buf;
	ret = ioctl(fd, UVCIOC_CTRL_QUERY, &q);
	printf("XUTRIG SET_CUR : ret=%d errno=%d (%s)\n", ret, errno,
	    strerror(errno));

	if (errno == EFAULT) {
		printf("XUTRIG: *** DF-5428 CONFIRMED: copyin() applied to the "
		    "kernel ioctl buffer -> unconditional EFAULT; the XU query "
		    "ioctl is dead ***\n");
		return (0);
	}
	printf("XUTRIG: unexpected errno (bug not manifest?)\n");
	return (1);
}