// https://syzkaller.appspot.com/bug?id=b2987f1840fd1eeacb46bb9cb7e3d3972c1c3b9e #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include 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 = 0x0cf2, .idProduct = 0x6250, .bcdDevice = 0x0100, .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, .bNumConfigurations = 1, }; struct { struct usb_config_descriptor config; struct usb_interface_descriptor intf; struct usb_endpoint_descriptor ep1; struct usb_endpoint_descriptor ep2; } __attribute__((packed)) config_desc = { .config = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .wTotalLength = sizeof(config_desc), .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = 0x80, .bMaxPower = 50, }, .intf = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = 0x08, .bInterfaceSubClass = 0x06, .bInterfaceProtocol = 0x50, .iInterface = 0, }, .ep1 = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 512, .bInterval = 0, }, .ep2 = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x02, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 512, .bInterval = 0, } }; struct thread_arg { int fd; int ep_in; int ep_out; }; void *ep_thread(void *arg) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGALRM); pthread_sigmask(SIG_BLOCK, &set, NULL); struct thread_arg *targ = arg; int fd = targ->fd; int ep_in = targ->ep_in; int ep_out = targ->ep_out; char out_buf[sizeof(struct usb_raw_ep_io) + 512]; struct usb_raw_ep_io *out_io = (struct usb_raw_ep_io *)out_buf; char in_buf[sizeof(struct usb_raw_ep_io) + 512]; struct usb_raw_ep_io *in_io = (struct usb_raw_ep_io *)in_buf; while (1) { out_io->ep = ep_out; out_io->flags = 0; out_io->length = 512; if (ioctl(fd, USB_RAW_IOCTL_EP_READ, out_io) < 0) { if (errno == EINTR) continue; if (errno == EBADF || errno == ENODEV) break; usleep(1000); continue; } if (out_io->length == 31) { uint32_t *sig = (uint32_t *)out_io->data; if (*sig == 0x43425355) { uint32_t tag = ((uint32_t *)out_io->data)[1]; uint32_t dlen = ((uint32_t *)out_io->data)[2]; uint8_t flags = out_io->data[12]; if (dlen > 0) { uint32_t remaining = dlen; if (flags & 0x80) { while (remaining > 0) { in_io->ep = ep_in; in_io->flags = 0; in_io->length = remaining > 512 ? 512 : remaining; memset(in_io->data, 0, in_io->length); if (ioctl(fd, USB_RAW_IOCTL_EP_WRITE, in_io) < 0) break; remaining -= in_io->length; } } else { while (remaining > 0) { out_io->ep = ep_out; out_io->flags = 0; out_io->length = remaining > 512 ? 512 : remaining; if (ioctl(fd, USB_RAW_IOCTL_EP_READ, out_io) < 0) break; remaining -= out_io->length; } } } in_io->ep = ep_in; in_io->flags = 0; in_io->length = 13; uint32_t *csw = (uint32_t *)in_io->data; csw[0] = 0x53425355; csw[1] = tag; csw[2] = 0; in_io->data[12] = 0; if (ioctl(fd, USB_RAW_IOCTL_EP_WRITE, in_io) < 0) { if (errno == EINTR) continue; if (errno == EBADF || errno == ENODEV) break; } } } } return NULL; } volatile int timeout_flag = 0; void sigalrm_handler(int sig) { timeout_flag = 1; } int do_test() { int fd = open("/dev/raw-gadget", O_RDWR); if (fd < 0) { printf("[-] open /dev/raw-gadget: %s\n", strerror(errno)); return 1; } struct usb_raw_init init = { .driver_name = "dummy_udc", .device_name = "dummy_udc.0", .speed = USB_SPEED_HIGH, }; if (ioctl(fd, USB_RAW_IOCTL_INIT, &init) < 0) { printf("[-] ioctl USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); close(fd); return 1; } if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) { printf("[-] ioctl USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); close(fd); return 1; } pthread_t t1; struct thread_arg targ1; int threads_started = 0; char io_buf[sizeof(struct usb_raw_ep_io) + 1024]; struct usb_raw_ep_io *io = (struct usb_raw_ep_io *)io_buf; char event_buf[sizeof(struct usb_raw_event) + 1024]; struct usb_raw_event *event = (struct usb_raw_event *)event_buf; timeout_flag = 0; alarm(3); while (!timeout_flag) { event->type = 0; event->length = 1024; if (ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) { if (errno == EINTR || timeout_flag) { printf("[+] Timeout, reconnecting...\n"); break; } printf("[-] ioctl USB_RAW_IOCTL_EVENT_FETCH: %s\n", strerror(errno)); break; } if (event->type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)&event->data[0]; if (ctrl->bRequestType == 0x80 && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { if (ctrl->wValue == (USB_DT_DEVICE << 8)) { io->ep = 0; io->flags = 0; io->length = ctrl->wLength < sizeof(dev_desc) ? ctrl->wLength : sizeof(dev_desc); memcpy(io->data, &dev_desc, io->length); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io); } else if (ctrl->wValue == (USB_DT_CONFIG << 8)) { io->ep = 0; io->flags = 0; io->length = ctrl->wLength < sizeof(config_desc) ? ctrl->wLength : sizeof(config_desc); memcpy(io->data, &config_desc, io->length); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io); } else { ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } } else if (ctrl->bRequestType == 0x00 && ctrl->bRequest == USB_REQ_SET_CONFIGURATION) { int ep1 = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &config_desc.ep1); int ep2 = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &config_desc.ep2); ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0); io->ep = 0; io->flags = 0; io->length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, io); if (!threads_started && ep1 >= 0 && ep2 >= 0) { targ1.fd = fd; targ1.ep_in = ep1; targ1.ep_out = ep2; pthread_create(&t1, NULL, ep_thread, &targ1); threads_started = 1; } } else if (ctrl->bRequestType == 0x00 && ctrl->bRequest == USB_REQ_SET_INTERFACE) { io->ep = 0; io->flags = 0; io->length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, io); } else if (ctrl->bRequest == USB_REQ_CLEAR_FEATURE) { io->ep = 0; io->flags = 0; io->length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, io); } else { ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } } } alarm(0); close(fd); if (threads_started) { pthread_join(t1, NULL); } return 0; } int main() { signal(SIGALRM, sigalrm_handler); for (int i = 0; i < 5; i++) { printf("[+] Iteration %d\n", i); do_test(); sleep(1); } return 0; }