// https://syzkaller.appspot.com/bug?id=38c196922107e173952b9d3d03242cb3172655dc #include #include #include #include #include #include #include #include #include #include #include #include #include void write_file_silent(const char *path, const char *value) { int fd = open(path, O_WRONLY); if (fd >= 0) { ssize_t len = strlen(value); ssize_t written = write(fd, value, len); (void)written; close(fd); } } int write_file(const char *path, const char *value) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return -1; } ssize_t len = strlen(value); ssize_t written = write(fd, value, len); if (written != len) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); close(fd); return -1; } close(fd); return 0; } char *find_udc_name() { DIR *dir = opendir("/sys/class/udc"); if (!dir) { printf("[-] Failed to opendir /sys/class/udc: %s\n", strerror(errno)); return NULL; } struct dirent *entry; static char udc_name[256]; char *found = NULL; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] != '.') { strncpy(udc_name, entry->d_name, sizeof(udc_name) - 1); found = udc_name; if (strstr(udc_name, "usbip-vudc")) { break; // Found the preferred UDC } } } closedir(dir); return found; } char *find_video_device() { DIR *dir = opendir("/sys/class/video4linux"); if (!dir) { printf("[-] Failed to opendir /sys/class/video4linux: %s\n", strerror(errno)); return NULL; } struct dirent *entry; static char dev_path[256]; while ((entry = readdir(dir)) != NULL) { if (strncmp(entry->d_name, "video", 5) == 0) { char name_path[512]; snprintf(name_path, sizeof(name_path), "/sys/class/video4linux/%s/name", entry->d_name); int fd = open(name_path, O_RDONLY); if (fd >= 0) { char name[256]; ssize_t bytes = read(fd, name, sizeof(name) - 1); if (bytes > 0) { name[bytes] = '\0'; if (strstr(name, "usbip-vudc") != NULL || strstr(name, "dummy_udc") != NULL || strstr(name, "UVC") != NULL) { close(fd); snprintf(dev_path, sizeof(dev_path), "/dev/%s", entry->d_name); closedir(dir); return dev_path; } } close(fd); } } } closedir(dir); return NULL; } void cleanup_gadget() { write_file_silent("/sys/kernel/config/usb_gadget/g1/UDC", "\n"); unlink("/sys/kernel/config/usb_gadget/g1/configs/c.1/uvc.usb0"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/fs/h"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/hs/h"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/ss/h"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/class/fs/h"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/class/ss/h"); unlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h/u"); rmdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h"); rmdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p"); rmdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u"); rmdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/header/h"); rmdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0"); rmdir("/sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409"); rmdir("/sys/kernel/config/usb_gadget/g1/configs/c.1"); rmdir("/sys/kernel/config/usb_gadget/g1/strings/0x409"); rmdir("/sys/kernel/config/usb_gadget/g1"); } int main() { struct stat st; if (stat("/sys/kernel/config", &st) < 0) { if (mkdir("/sys/kernel/config", 0755) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config: %s\n", strerror(errno)); } } if (mount("none", "/sys/kernel/config", "configfs", 0, NULL) < 0) { if (errno != EBUSY) { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); exit(1); } } printf("[+] configfs mounted or already mounted.\n"); cleanup_gadget(); if (mkdir("/sys/kernel/config/usb_gadget/g1", 0755) < 0) { printf("[-] Failed to mkdir g1: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir g1 successful.\n"); if (write_file("/sys/kernel/config/usb_gadget/g1/idVendor", "0x1d6b\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/idProduct", "0x0104\n") < 0) exit(1); if (mkdir("/sys/kernel/config/usb_gadget/g1/strings/0x409", 0755) < 0) { printf("[-] Failed to mkdir strings/0x409: %s\n", strerror(errno)); exit(1); } if (write_file("/sys/kernel/config/usb_gadget/g1/strings/0x409/serialnumber", "1234567890\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/strings/0x409/manufacturer", "Dummy\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/strings/0x409/product", "UVC Gadget\n") < 0) exit(1); if (mkdir("/sys/kernel/config/usb_gadget/g1/configs/c.1", 0755) < 0) { printf("[-] Failed to mkdir configs/c.1: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409", 0755) < 0) { printf("[-] Failed to mkdir configs/c.1/strings/0x409: %s\n", strerror(errno)); exit(1); } if (write_file("/sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409/configuration", "Config 1\n") < 0) exit(1); if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0", 0755) < 0) { printf("[-] Failed to mkdir functions/uvc.usb0: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u", 0755) < 0) { printf("[-] Failed to mkdir streaming/uncompressed/u: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p", 0755) < 0) { printf("[-] Failed to mkdir streaming/uncompressed/u/360p: %s\n", strerror(errno)); exit(1); } if (write_file("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p/wWidth", "640\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p/wHeight", "360\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p/dwMaxVideoFrameBufferSize", "460800\n") < 0) exit(1); if (write_file("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval", "333333\n") < 0) exit(1); if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h", 0755) < 0) { printf("[-] Failed to mkdir streaming/header/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/uncompressed/u", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h/u") < 0) { printf("[-] Failed to symlink streaming/header/h/u: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/fs/h") < 0) { printf("[-] Failed to symlink streaming/class/fs/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/hs/h") < 0) { printf("[-] Failed to symlink streaming/class/hs/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/header/h", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/streaming/class/ss/h") < 0) { printf("[-] Failed to symlink streaming/class/ss/h: %s\n", strerror(errno)); exit(1); } if (mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/header/h", 0755) < 0) { printf("[-] Failed to mkdir control/header/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/header/h", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/class/fs/h") < 0) { printf("[-] Failed to symlink control/class/fs/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/header/h", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/control/class/ss/h") < 0) { printf("[-] Failed to symlink control/class/ss/h: %s\n", strerror(errno)); exit(1); } if (symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0", "/sys/kernel/config/usb_gadget/g1/configs/c.1/uvc.usb0") < 0) { printf("[-] Failed to symlink configs/c.1/uvc.usb0: %s\n", strerror(errno)); exit(1); } printf("[+] UVC gadget configured successfully.\n"); char *udc_name = find_udc_name(); if (!udc_name) { printf("[-] Failed to find UDC name.\n"); exit(1); } printf("[+] Found UDC: %s\n", udc_name); char udc_buf[256]; snprintf(udc_buf, sizeof(udc_buf), "%s\n", udc_name); if (write_file("/sys/kernel/config/usb_gadget/g1/UDC", udc_buf) < 0) { printf("[-] Failed to write UDC.\n"); exit(1); } printf("[+] Bound to UDC successfully.\n"); char *dev_path = NULL; for (int i = 0; i < 50; i++) { dev_path = find_video_device(); if (dev_path) break; usleep(100000); } if (!dev_path) { printf("[-] Failed to find created video device.\n"); exit(1); } printf("[+] Found video device: %s\n", dev_path); int fd = open(dev_path, O_RDWR); if (fd < 0) { printf("[-] Failed to open video device: %s\n", strerror(errno)); exit(1); } printf("[+] Opened video device successfully.\n"); struct v4l2_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 360; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_NONE; fmt.fmt.pix.bytesperline = 640 * 2; fmt.fmt.pix.sizeimage = 640 * 360 * 2; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { printf("[-] Failed to VIDIOC_S_FMT: %s\n", strerror(errno)); } else { printf("[+] VIDIOC_S_FMT successful.\n"); } struct v4l2_requestbuffers req; memset(&req, 0, sizeof(req)); req.count = 2; req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { printf("[-] Failed to VIDIOC_REQBUFS: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] VIDIOC_REQBUFS successful.\n"); for (unsigned int i = 0; i < req.count; i++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { printf("[-] Failed to VIDIOC_QUERYBUF: %s\n", strerror(errno)); close(fd); exit(1); } void *map = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (map == MAP_FAILED) { printf("[-] Failed to mmap: %s\n", strerror(errno)); close(fd); exit(1); } if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { printf("[-] Failed to VIDIOC_QBUF: %s\n", strerror(errno)); close(fd); exit(1); } } printf("[+] Buffers mapped and queued successfully.\n"); // Trigger the crash: STREAMON before the host has selected the alt setting enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT; printf("[*] Calling VIDIOC_STREAMON...\n"); if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { printf("[-] Failed to VIDIOC_STREAMON: %s\n", strerror(errno)); } else { printf("[+] VIDIOC_STREAMON successful.\n"); } close(fd); // Wait for asynchronous tasks sleep(2); cleanup_gadget(); printf("[+] Done.\n"); return 0; }