// https://syzkaller.appspot.com/bug?id=5073831ae6fda2a5f8332c1075c7474f260a49f7 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_include() #include #endif #ifndef USB_RAW_IOCTL_INIT #define UDC_NAME_LENGTH_MAX 128 struct usb_raw_init { __u8 driver_name[UDC_NAME_LENGTH_MAX]; __u8 device_name[UDC_NAME_LENGTH_MAX]; __u8 speed; }; enum usb_raw_event_type { USB_RAW_EVENT_INVALID = 0, USB_RAW_EVENT_CONNECT = 1, USB_RAW_EVENT_CONTROL = 2, USB_RAW_EVENT_SUSPEND = 3, USB_RAW_EVENT_RESUME = 4, USB_RAW_EVENT_RESET = 5, USB_RAW_EVENT_DISCONNECT = 6, }; struct usb_raw_event { __u32 type; __u32 length; __u8 data[]; }; struct usb_raw_ep_io { __u16 ep; __u16 flags; __u32 length; __u8 data[]; }; #define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init) #define USB_RAW_IOCTL_RUN _IO('U', 1) #define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event) #define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io) #define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io) #define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) #define USB_RAW_IOCTL_CONFIGURE _IO('U', 9) #define USB_RAW_IOCTL_EP0_STALL _IO('U', 12) #endif #ifndef USB_SPEED_HIGH #define USB_SPEED_HIGH 3 #endif struct host_controller_info { uint8_t info_size; uint16_t firmware_version; uint8_t number_of_ports; } __attribute__((packed)); struct sd_status_header { uint8_t header_size; uint8_t header_type; uint8_t port_number; uint16_t port_flags; uint32_t sdio_clock; uint16_t host_header_size; uint16_t func_header_size; uint16_t ctrl_header_size; } __attribute__((packed)); struct usb_endpoint_descriptor_7 { uint8_t bLength; uint8_t bDescriptorType; uint8_t bEndpointAddress; uint8_t bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; } __attribute__((packed)); struct vub300_config_desc { struct usb_config_descriptor config; struct usb_interface_descriptor iface; struct usb_endpoint_descriptor_7 ep[4]; } __attribute__((packed)); struct raw_ep_io_data { struct usb_raw_ep_io io; uint8_t data[256]; }; struct raw_event_buf { struct usb_raw_event event; uint8_t data[256]; }; static struct usb_device_descriptor dev_desc; static struct vub300_config_desc conf_desc; static volatile int stop_ep0 = 0; static volatile int got_port_status = 0; static void sigusr1_handler(int sig) { (void)sig; } static void init_descriptors(void) { memset(&dev_desc, 0, sizeof(dev_desc)); dev_desc.bLength = sizeof(dev_desc); dev_desc.bDescriptorType = USB_DT_DEVICE; dev_desc.bcdUSB = htole16(0x0200); dev_desc.bDeviceClass = 0; dev_desc.bDeviceSubClass = 0; dev_desc.bDeviceProtocol = 0; dev_desc.bMaxPacketSize0 = 64; dev_desc.idVendor = htole16(0x0424); /* VUB300_VENDOR_ID */ dev_desc.idProduct = htole16(0x012c); /* VUB300_PRODUCT_ID */ dev_desc.bcdDevice = htole16(0x0100); dev_desc.iManufacturer = 0; dev_desc.iProduct = 0; dev_desc.iSerialNumber = 0; dev_desc.bNumConfigurations = 1; memset(&conf_desc, 0, sizeof(conf_desc)); conf_desc.config.bLength = sizeof(conf_desc.config); conf_desc.config.bDescriptorType = USB_DT_CONFIG; conf_desc.config.wTotalLength = htole16(sizeof(conf_desc)); conf_desc.config.bNumInterfaces = 1; conf_desc.config.bConfigurationValue = 1; conf_desc.config.iConfiguration = 0; conf_desc.config.bmAttributes = 0x80; conf_desc.config.bMaxPower = 50; conf_desc.iface.bLength = sizeof(conf_desc.iface); conf_desc.iface.bDescriptorType = USB_DT_INTERFACE; conf_desc.iface.bInterfaceNumber = 0; conf_desc.iface.bAlternateSetting = 0; conf_desc.iface.bNumEndpoints = 4; conf_desc.iface.bInterfaceClass = 0xff; conf_desc.iface.bInterfaceSubClass = 0xff; conf_desc.iface.bInterfaceProtocol = 0xff; conf_desc.iface.iInterface = 0; /* Endpoint 0: 0x81 (Bulk IN) -> vub300->cmnd_res_ep */ conf_desc.ep[0].bLength = sizeof(struct usb_endpoint_descriptor_7); conf_desc.ep[0].bDescriptorType = USB_DT_ENDPOINT; conf_desc.ep[0].bEndpointAddress = 0x81; conf_desc.ep[0].bmAttributes = USB_ENDPOINT_XFER_BULK; conf_desc.ep[0].wMaxPacketSize = htole16(512); conf_desc.ep[0].bInterval = 0; /* Endpoint 1: 0x02 (Bulk OUT) -> vub300->cmnd_out_ep */ conf_desc.ep[1].bLength = sizeof(struct usb_endpoint_descriptor_7); conf_desc.ep[1].bDescriptorType = USB_DT_ENDPOINT; conf_desc.ep[1].bEndpointAddress = 0x02; conf_desc.ep[1].bmAttributes = USB_ENDPOINT_XFER_BULK; conf_desc.ep[1].wMaxPacketSize = htole16(512); conf_desc.ep[1].bInterval = 0; /* Endpoint 2: 0x86 (Bulk IN) -> vub300->data_inp_ep */ conf_desc.ep[2].bLength = sizeof(struct usb_endpoint_descriptor_7); conf_desc.ep[2].bDescriptorType = USB_DT_ENDPOINT; conf_desc.ep[2].bEndpointAddress = 0x86; conf_desc.ep[2].bmAttributes = USB_ENDPOINT_XFER_BULK; conf_desc.ep[2].wMaxPacketSize = htole16(512); conf_desc.ep[2].bInterval = 0; /* Endpoint 3: 0x07 (Bulk OUT) -> vub300->data_out_ep */ conf_desc.ep[3].bLength = sizeof(struct usb_endpoint_descriptor_7); conf_desc.ep[3].bDescriptorType = USB_DT_ENDPOINT; conf_desc.ep[3].bEndpointAddress = 0x07; conf_desc.ep[3].bmAttributes = USB_ENDPOINT_XFER_BULK; conf_desc.ep[3].wMaxPacketSize = htole16(512); conf_desc.ep[3].bInterval = 0; } static int ep_enable(int fd, uint8_t addr, uint16_t maxpacket) { struct usb_endpoint_descriptor ep; memset(&ep, 0, sizeof(ep)); ep.bLength = USB_DT_ENDPOINT_SIZE; ep.bDescriptorType = USB_DT_ENDPOINT; ep.bEndpointAddress = addr; ep.bmAttributes = USB_ENDPOINT_XFER_BULK; ep.wMaxPacketSize = htole16(maxpacket); ep.bInterval = 0; return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, (unsigned long)&ep); } static int ep0_write(int fd, const void *data, uint32_t len) { struct raw_ep_io_data req; memset(&req, 0, sizeof(req)); req.io.ep = 0; req.io.flags = 0; req.io.length = len; if (len > 0 && data) memcpy(req.data, data, len); int res = ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &req.io); if (res < 0) { if (!stop_ep0) printf("[-] ioctl USB_RAW_IOCTL_EP0_WRITE failed: %s\n", strerror(errno)); return -1; } return res; } static int ep0_read(int fd, uint32_t len) { struct raw_ep_io_data req; memset(&req, 0, sizeof(req)); req.io.ep = 0; req.io.flags = 0; req.io.length = len; int res = ioctl(fd, USB_RAW_IOCTL_EP0_READ, &req.io); if (res < 0) { if (!stop_ep0) printf("[-] ioctl USB_RAW_IOCTL_EP0_READ failed: %s\n", strerror(errno)); return -1; } return res; } static int ep0_stall(int fd) { int res = ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); if (res < 0) { if (!stop_ep0) printf("[-] ioctl USB_RAW_IOCTL_EP0_STALL failed: %s\n", strerror(errno)); return -1; } return res; } static void *ep0_loop(void *arg) { int fd = *(int *)arg; printf("[+] ep0_loop thread started.\n"); while (!stop_ep0) { struct raw_event_buf ev; memset(&ev, 0, sizeof(ev)); ev.event.type = 0; ev.event.length = sizeof(ev.data); int res = ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, &ev.event); if (res < 0) { if (stop_ep0 || errno == EINTR) break; printf("[-] USB_RAW_IOCTL_EVENT_FETCH failed: %s\n", strerror(errno)); break; } if (ev.event.type == USB_RAW_EVENT_CONNECT) { printf("[+] USB event: CONNECT\n"); } else if (ev.event.type == USB_RAW_EVENT_RESET) { printf("[+] USB event: RESET\n"); } else if (ev.event.type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)ev.event.data; uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK; uint16_t wLength = le16toh(ctrl->wLength); uint16_t wValue = le16toh(ctrl->wValue); if (req_type == USB_TYPE_STANDARD) { if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { uint8_t desc_type = wValue >> 8; if (desc_type == USB_DT_DEVICE) { printf("[+] Answering GET_DESCRIPTOR (Device, len=%u)\n", wLength); uint32_t len = sizeof(dev_desc); if (len > wLength) len = wLength; ep0_write(fd, &dev_desc, len); } else if (desc_type == USB_DT_CONFIG) { printf("[+] Answering GET_DESCRIPTOR (Config, len=%u, conf_desc size=%zu)\n", wLength, sizeof(conf_desc)); uint32_t len = sizeof(conf_desc); if (len > wLength) len = wLength; ep0_write(fd, &conf_desc, len); } else { printf("[*] Stalling unhandled descriptor type 0x%02x\n", desc_type); ep0_stall(fd); } } else if (ctrl->bRequest == USB_REQ_SET_ADDRESS) { printf("[+] Answering SET_ADDRESS (addr=%u)\n", wValue); ep0_read(fd, 0); } else if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) { printf("[+] Answering SET_CONFIGURATION (val=%u)\n", wValue); int c_res = ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0); if (c_res < 0) printf("[-] Failed USB_RAW_IOCTL_CONFIGURE: %s\n", strerror(errno)); else printf("[+] USB_RAW_IOCTL_CONFIGURE successful\n"); uint8_t ep_addrs[4] = {0x81, 0x02, 0x86, 0x07}; for (int i = 0; i < 4; i++) { int ep_res = ep_enable(fd, ep_addrs[i], 512); if (ep_res < 0) printf("[-] Failed to enable endpoint %d (0x%02x): %s\n", i, ep_addrs[i], strerror(errno)); else printf("[+] Endpoint %d (0x%02x) enabled successfully\n", i, ep_addrs[i]); } ep0_read(fd, 0); } else if (ctrl->bRequest == USB_REQ_GET_STATUS) { printf("[+] Answering GET_STATUS\n"); uint16_t status = 0; uint32_t len = sizeof(status); if (len > wLength) len = wLength; ep0_write(fd, &status, len); } else { printf("[*] Standard request 0x%02x (dir_in=%d, len=%u)\n", ctrl->bRequest, !!(ctrl->bRequestType & USB_DIR_IN), wLength); if (ctrl->bRequestType & USB_DIR_IN) { uint8_t zero[wLength > 0 ? wLength : 1]; memset(zero, 0, sizeof(zero)); ep0_write(fd, zero, wLength); } else { ep0_read(fd, wLength); } } } else if (req_type == USB_TYPE_VENDOR) { if (ctrl->bRequest == 1) { /* GET_HC_INF0 */ printf("[+] Answering GET_HC_INF0 (len=%u)\n", wLength); struct host_controller_info hc = { .info_size = sizeof(struct host_controller_info), .firmware_version = htole16(0x0100), .number_of_ports = 1, }; uint32_t len = sizeof(hc); if (len > wLength) len = wLength; ep0_write(fd, &hc, len); } else if (ctrl->bRequest == 16) { /* SET_ROM_WAIT_STATES */ printf("[+] Answering SET_ROM_WAIT_STATES (len=%u)\n", wLength); ep0_read(fd, wLength); } else if (ctrl->bRequest == 0) { /* GET_SYSTEM_PORT_STATUS */ printf("[+] Answering GET_SYSTEM_PORT_STATUS (len=%u)\n", wLength); struct sd_status_header status; memset(&status, 0, sizeof(status)); status.header_size = sizeof(status); status.port_number = 1; status.port_flags = 0; /* card_present = 0 */ uint32_t len = sizeof(status); if (len > wLength) len = wLength; ep0_write(fd, &status, len); got_port_status = 1; } else if (ctrl->bRequest == 4) { /* SET_SD_POWER */ printf("[+] Answering SET_SD_POWER (len=%u)\n", wLength); ep0_read(fd, wLength); } else if (ctrl->bRequest == 11) { /* SET_CLOCK_SPEED */ printf("[+] Answering SET_CLOCK_SPEED (len=%u)\n", wLength); ep0_read(fd, wLength); } else { printf("[*] Other vendor request 0x%02x (dir_in=%d, len=%u)\n", ctrl->bRequest, !!(ctrl->bRequestType & USB_DIR_IN), wLength); if (ctrl->bRequestType & USB_DIR_IN) { uint8_t zero[wLength > 0 ? wLength : 1]; memset(zero, 0, sizeof(zero)); ep0_write(fd, zero, wLength); } else { ep0_read(fd, wLength); } } } else { printf("[*] Other request type 0x%02x (bRequest=0x%02x, len=%u)\n", ctrl->bRequestType, ctrl->bRequest, wLength); if (ctrl->bRequestType & USB_DIR_IN) { uint8_t zero[wLength > 0 ? wLength : 1]; memset(zero, 0, sizeof(zero)); ep0_write(fd, zero, wLength); } else { ep0_read(fd, wLength); } } } } printf("[+] ep0_loop thread exiting.\n"); return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for vub300 sleeping in atomic context bug...\n"); signal(SIGUSR1, sigusr1_handler); init_descriptors(); 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("[+] Opened /dev/raw-gadget successfully (fd=%d).\n", fd); DIR *dir = opendir("/sys/class/udc"); if (!dir) { printf("[-] Failed to opendir /sys/class/udc: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] Opened /sys/class/udc successfully.\n"); struct dirent *ent; char driver_name[UDC_NAME_LENGTH_MAX] = "dummy_udc"; char device_name[UDC_NAME_LENGTH_MAX] = "dummy_udc.0"; bool found_udc = false; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; strncpy(device_name, ent->d_name, sizeof(device_name) - 1); strncpy(driver_name, ent->d_name, sizeof(driver_name) - 1); char *dot = strchr(driver_name, '.'); if (dot) *dot = '\0'; found_udc = true; printf("[+] Found UDC: device='%s', driver='%s'\n", device_name, driver_name); break; } int res = closedir(dir); if (res < 0) { printf("[-] Failed to closedir /sys/class/udc: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] Closed /sys/class/udc directory.\n"); if (!found_udc) { printf("[-] No UDC found in /sys/class/udc, using defaults.\n"); } struct usb_raw_init init; memset(&init, 0, sizeof(init)); strncpy((char *)init.driver_name, driver_name, sizeof(init.driver_name) - 1); strncpy((char *)init.device_name, device_name, sizeof(init.device_name) - 1); init.speed = USB_SPEED_HIGH; res = ioctl(fd, USB_RAW_IOCTL_INIT, &init); if (res < 0) { printf("[-] Failed ioctl USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] Initialized raw-gadget for UDC '%s'.\n", device_name); /* Crucial: USB_RAW_IOCTL_RUN takes an unsigned long value which must be 0 */ res = ioctl(fd, USB_RAW_IOCTL_RUN, 0); if (res < 0) { printf("[-] Failed ioctl USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] Started raw-gadget emulation (USB_RAW_IOCTL_RUN).\n"); pthread_t ep0_tid; res = pthread_create(&ep0_tid, NULL, ep0_loop, &fd); if (res != 0) { printf("[-] Failed pthread_create: %s\n", strerror(res)); close(fd); exit(1); } printf("[+] Started ep0 handler thread.\n"); printf("[+] Waiting for vub300 enumeration and initialization...\n"); int waited_ms = 0; while (waited_ms < 5000) { if (got_port_status) { printf("[+] GET_SYSTEM_PORT_STATUS answered. Waiting 200ms for mmc_add_host to complete...\n"); usleep(200000); break; } usleep(20000); waited_ms += 20; } if (!got_port_status) { printf("[-] vub300 probe did not reach GET_SYSTEM_PORT_STATUS within timeout.\n"); stop_ep0 = 1; pthread_kill(ep0_tid, SIGUSR1); pthread_join(ep0_tid, NULL); close(fd); exit(1); } printf("[+] vub300 probe completed successfully. Inactivity timer is armed.\n"); printf("[+] Triggering device disconnect by stopping ep0 and closing raw-gadget...\n"); stop_ep0 = 1; pthread_kill(ep0_tid, SIGUSR1); struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 1; res = pthread_timedjoin_np(ep0_tid, NULL, &ts); if (res != 0 && res != ETIMEDOUT) { printf("[-] Failed pthread_timedjoin_np: %s\n", strerror(res)); close(fd); exit(1); } printf("[+] ep0 thread joined successfully.\n"); res = close(fd); if (res < 0) { printf("[-] Failed to close /dev/raw-gadget: %s\n", strerror(errno)); exit(1); } printf("[+] /dev/raw-gadget closed successfully (simulating USB disconnect).\n"); printf("[+] Waiting 3 seconds for inactivity timer to expire in softirq context...\n"); sleep(3); printf("[+] Inactivity timer period elapsed.\n"); return 0; }