// https://syzkaller.appspot.com/bug?id=f463780e4ce584516f086413f42505e202bc3e8e #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef htole16 #if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) #define htole16(x) __builtin_bswap16(x) #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define htole16(x) __builtin_bswap16(x) #else #define htole16(x) (x) #endif #define MCT_U232_VID 0x0711 #define MCT_U232_PID 0x0210 #define MCT_U232_SET_MODEM_CTRL_REQUEST 10 static struct usb_device_descriptor device_desc = { .bLength = sizeof(device_desc), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = htole16(0x0200), .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = htole16(MCT_U232_VID), .idProduct = htole16(MCT_U232_PID), .bcdDevice = htole16(0x0102), .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, .bNumConfigurations = 1, }; static struct { struct usb_config_descriptor config; struct usb_interface_descriptor iface; struct usb_endpoint_descriptor ep1; struct usb_endpoint_descriptor ep2; struct usb_endpoint_descriptor ep3; } __attribute__((packed)) config_desc = { .config = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .wTotalLength = htole16(sizeof(config_desc)), .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = 0x80, .bMaxPower = 50, }, .iface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 3, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, }, .ep1 = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = htole16(64), .bInterval = 2, }, .ep2 = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x82, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = htole16(64), .bInterval = 2, }, .ep3 = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = htole16(64), .bInterval = 0, }, }; static volatile int g_configured = 0; static volatile int g_stop = 0; static volatile int g_raw_fd = -1; static void sigusr1_handler(int sig) { (void)sig; } static int ep0_write(int fd, const void *data, uint32_t len) { struct { struct usb_raw_ep_io io; uint8_t buf[256]; } req; memset(&req, 0, sizeof(req)); req.io.ep = 0; req.io.flags = 0; req.io.length = len; if (data && len > 0) { if (len > sizeof(req.buf)) len = sizeof(req.buf); memcpy(req.buf, data, len); } int res = ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &req); if (res < 0) { printf("[-] ep0_write (len=%u) failed: %s\n", len, strerror(errno)); } return res; } static int ep0_read(int fd, void *data, uint32_t len) { struct { struct usb_raw_ep_io io; uint8_t buf[256]; } 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); if (res < 0) { printf("[-] ep0_read (len=%u) failed: %s\n", len, strerror(errno)); return res; } if (res > 0 && data && len > 0) { uint32_t to_copy = (res < (int)len) ? (uint32_t)res : len; memcpy(data, req.buf, to_copy); } return res; } static int ep0_stall(int fd) { return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } static void *gadget_thread(void *arg) { int fd = (int)(long)arg; g_raw_fd = fd; printf("[+] Gadget event loop started.\n"); while (!g_stop) { struct { struct usb_raw_event event; uint8_t data[256]; } 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 (errno == EINTR) { if (g_stop) break; continue; } printf("[-] EVENT_FETCH failed: %s\n", strerror(errno)); break; } if (ev.event.type == USB_RAW_EVENT_CONNECT) { printf("[+] USB_RAW_EVENT_CONNECT received.\n"); continue; } if (ev.event.type == USB_RAW_EVENT_RESET) { printf("[+] USB_RAW_EVENT_RESET received.\n"); continue; } if (ev.event.type == USB_RAW_EVENT_DISCONNECT) { printf("[+] USB_RAW_EVENT_DISCONNECT received.\n"); break; } 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; printf("[+] Control event: bRequestType=0x%02x, bRequest=%u, wLength=%u\n", ctrl->bRequestType, ctrl->bRequest, le16toh(ctrl->wLength)); if (((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) && ctrl->bRequest == MCT_U232_SET_MODEM_CTRL_REQUEST) { printf("[+] Received MCT_U232_SET_MODEM_CTRL_REQUEST (10)! Closing raw_fd to trigger bug...\n"); close(fd); g_raw_fd = -1; g_stop = 1; return NULL; } if (req_type == USB_TYPE_STANDARD) { if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { uint8_t desc_type = ctrl->wValue >> 8; uint16_t wLength = le16toh(ctrl->wLength); if (desc_type == USB_DT_DEVICE) { uint32_t len = sizeof(device_desc); if (len > wLength) len = wLength; printf("[+] Handling GET_DESCRIPTOR (DEVICE), len=%u\n", len); ep0_write(fd, &device_desc, len); } else if (desc_type == USB_DT_CONFIG) { uint32_t len = sizeof(config_desc); if (len > wLength) len = wLength; printf("[+] Handling GET_DESCRIPTOR (CONFIG), len=%u\n", len); ep0_write(fd, &config_desc, len); } else { printf("[+] Unsupported descriptor type %u, stalling\n", desc_type); ep0_stall(fd); } } else if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) { printf("[+] Handling SET_CONFIGURATION (val=%u)\n", le16toh(ctrl->wValue)); int ep1 = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, (void *)&config_desc.ep1); if (ep1 < 0) printf("[-] Failed to enable ep1: %s\n", strerror(errno)); else printf("[+] Enabled ep1 (handle=%d)\n", ep1); int ep2 = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, (void *)&config_desc.ep2); if (ep2 < 0) printf("[-] Failed to enable ep2: %s\n", strerror(errno)); else printf("[+] Enabled ep2 (handle=%d)\n", ep2); int ep3 = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, (void *)&config_desc.ep3); if (ep3 < 0) printf("[-] Failed to enable ep3: %s\n", strerror(errno)); else printf("[+] Enabled ep3 (handle=%d)\n", ep3); int cfg = ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0); if (cfg < 0) printf("[-] Failed to configure raw gadget: %s\n", strerror(errno)); else printf("[+] Configured raw gadget successfully.\n"); int ack = ep0_read(fd, NULL, 0); if (ack < 0) printf("[-] ep0_read for SET_CONFIGURATION failed: %s\n", strerror(errno)); else printf("[+] ep0_read for SET_CONFIGURATION acknowledged (ret=%d)\n", ack); g_configured = 1; } else if (ctrl->bRequest == USB_REQ_SET_INTERFACE) { printf("[+] Handling SET_INTERFACE\n"); ep0_read(fd, NULL, 0); } else if (ctrl->bRequest == USB_REQ_GET_STATUS) { uint16_t status = 0; ep0_write(fd, &status, sizeof(status)); } else { printf("[+] Unhandled standard request %u, stalling\n", ctrl->bRequest); ep0_stall(fd); } } else { printf("[+] Unhandled non-standard request (type=0x%x, req=%u), stalling\n", ctrl->bRequestType, ctrl->bRequest); ep0_stall(fd); } } } printf("[+] Gadget thread exiting.\n"); return NULL; } static void *open_thread(void *arg) { (void)arg; printf("[+] Open thread waiting for device configuration...\n"); for (int i = 0; i < 500 && !g_configured && !g_stop; i++) { usleep(10000); } if (g_stop || !g_configured) { printf("[-] Device was not configured in time.\n"); return NULL; } printf("[+] Device configured! Waiting for /sys/class/tty/ttyUSB0...\n"); int ready = 0; for (int i = 0; i < 500 && !g_stop; i++) { if (access("/sys/class/tty/ttyUSB0", F_OK) == 0) { printf("[+] /sys/class/tty/ttyUSB0 detected after %d ms!\n", i * 10); ready = 1; break; } usleep(10000); } if (!ready) { printf("[-] /sys/class/tty/ttyUSB0 did not appear, polling /dev/ttyUSB0 anyway...\n"); } printf("[+] Calling open(/dev/ttyUSB0, O_RDWR | O_NONBLOCK)...\n"); for (int i = 0; i < 50 && !g_stop; i++) { int fd = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK); if (fd >= 0) { printf("[+] open(/dev/ttyUSB0) succeeded (fd=%d)\n", fd); close(fd); break; } if (errno != ENODEV && errno != ENXIO && errno != ENOENT) { printf("[-] open(/dev/ttyUSB0) failed: %s\n", strerror(errno)); break; } usleep(10000); } return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[+] Starting mct_u232_open reproducer...\n"); signal(SIGUSR1, sigusr1_handler); int res = mknod("/dev/ttyUSB0", S_IFCHR | 0666, makedev(188, 0)); if (res < 0 && errno != EEXIST) { printf("[-] mknod /dev/ttyUSB0 failed: %s\n", strerror(errno)); } else { printf("[+] /dev/ttyUSB0 character device node prepared.\n"); } DIR *dir = opendir("/sys/class/udc"); if (!dir) { printf("[-] Failed to opendir /sys/class/udc: %s\n", strerror(errno)); exit(1); } printf("[+] Opened /sys/class/udc successfully.\n"); struct dirent *ent; char udc_name[64] = {0}; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; printf("[+] Found UDC: %s\n", ent->d_name); if (udc_name[0] == '\0') { strncpy(udc_name, ent->d_name, sizeof(udc_name) - 1); } } closedir(dir); if (udc_name[0] == '\0') { printf("[-] No UDC found, defaulting to dummy_udc.0\n"); strncpy(udc_name, "dummy_udc.0", sizeof(udc_name) - 1); } for (int iter = 0; iter < 5; iter++) { printf("\n[+] --- Reproduction attempt %d ---\n", iter + 1); g_configured = 0; g_stop = 0; int raw_fd = open("/dev/raw-gadget", O_RDWR); if (raw_fd < 0) { printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno)); exit(1); } printf("[+] Opened /dev/raw-gadget successfully (fd=%d).\n", raw_fd); struct usb_raw_init init_arg; memset(&init_arg, 0, sizeof(init_arg)); strncpy((char *)init_arg.driver_name, "dummy_udc", sizeof(init_arg.driver_name) - 1); strncpy((char *)init_arg.device_name, udc_name, sizeof(init_arg.device_name) - 1); init_arg.speed = USB_SPEED_FULL; res = ioctl(raw_fd, USB_RAW_IOCTL_INIT, &init_arg); if (res < 0) { printf("[-] Failed to ioctl(USB_RAW_IOCTL_INIT): %s\n", strerror(errno)); close(raw_fd); exit(1); } printf("[+] USB_RAW_IOCTL_INIT successful.\n"); res = ioctl(raw_fd, USB_RAW_IOCTL_RUN, 0); if (res < 0) { printf("[-] Failed to ioctl(USB_RAW_IOCTL_RUN): %s\n", strerror(errno)); close(raw_fd); exit(1); } printf("[+] USB_RAW_IOCTL_RUN successful.\n"); pthread_t th_gadget, th_open; res = pthread_create(&th_gadget, NULL, gadget_thread, (void *)(long)raw_fd); if (res != 0) { printf("[-] Failed to create gadget thread: %s\n", strerror(res)); close(raw_fd); exit(1); } printf("[+] Created gadget thread successfully.\n"); res = pthread_create(&th_open, NULL, open_thread, NULL); if (res != 0) { printf("[-] Failed to create open thread: %s\n", strerror(res)); g_stop = 1; pthread_kill(th_gadget, SIGUSR1); pthread_join(th_gadget, NULL); exit(1); } printf("[+] Created open thread successfully.\n"); pthread_join(th_open, NULL); printf("[+] Open thread joined.\n"); sleep(1); g_stop = 1; if (g_raw_fd >= 0) { close(g_raw_fd); g_raw_fd = -1; } pthread_kill(th_gadget, SIGUSR1); pthread_join(th_gadget, NULL); printf("[+] Gadget thread joined.\n"); sleep(1); } printf("[+] All reproduction attempts completed. Waiting before exit...\n"); sleep(3); return 0; }