DF-5428 / xu_trig.c
/* * 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); } |