// https://syzkaller.appspot.com/bug?id=8412ff473764c4632d30217105c60459df167ad5 #include #include #include #include #include #include #include #include #include #include #include void setup_failslab_global() { int fd; fd = open("/sys/kernel/debug/failslab/verbose", O_WRONLY); if (fd >= 0) { if (write(fd, "0\n", 2) < 0) { printf("[-] Failed to write to failslab verbose: %s\n", strerror(errno)); } close(fd); } else { printf("[-] Failed to open failslab verbose: %s\n", strerror(errno)); } fd = open("/sys/kernel/debug/failslab/task-filter", O_WRONLY); if (fd >= 0) { if (write(fd, "N\n", 2) < 0) { printf("[-] Failed to write to failslab task-filter: %s\n", strerror(errno)); } close(fd); } else { printf("[-] Failed to open failslab task-filter: %s\n", strerror(errno)); } printf("[+] setup_failslab_global successful.\n"); } void enable_failslab() { int fd_prob = open("/sys/kernel/debug/failslab/probability", O_WRONLY); int fd_times = open("/sys/kernel/debug/failslab/times", O_WRONLY); if (fd_prob >= 0 && fd_times >= 0) { if (write(fd_prob, "15\n", 3) < 0) { printf("[-] Failed to write to failslab probability: %s\n", strerror(errno)); } if (write(fd_times, "1\n", 2) < 0) { printf("[-] Failed to write to failslab times: %s\n", strerror(errno)); } } else { printf("[-] Failed to open failslab probability/times: %s\n", strerror(errno)); } if (fd_prob >= 0) close(fd_prob); if (fd_times >= 0) close(fd_times); } void disable_failslab() { int fd_prob = open("/sys/kernel/debug/failslab/probability", O_WRONLY); if (fd_prob >= 0) { if (write(fd_prob, "0\n", 2) < 0) { printf("[-] Failed to write to failslab probability: %s\n", strerror(errno)); } close(fd_prob); } } void run_usb_gadget_once() { int fd = open("/dev/raw-gadget", O_RDWR); if (fd < 0) { printf("[-] Failed to open /dev/raw-gadget: %s\n", strerror(errno)); exit(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("[-] Failed to ioctl USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); exit(1); } if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) { printf("[-] Failed to ioctl USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); exit(1); } char io_buf[1024]; struct usb_raw_ep_io *io = (struct usb_raw_ep_io *)io_buf; int connected = 1; while (connected) { char event_buf[sizeof(struct usb_raw_event) + sizeof(struct usb_ctrlrequest)]; struct usb_raw_event *event = (struct usb_raw_event *)event_buf; event->length = sizeof(struct usb_ctrlrequest); int ret = ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event); if (ret < 0) { break; } if (event->type == USB_RAW_EVENT_CONTROL) { struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event->data; io->ep = 0; io->flags = 0; if (ctrl->bRequestType == 0x80 && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { int desc_type = ctrl->wValue >> 8; if (desc_type == USB_DT_DEVICE) { struct usb_device_descriptor desc = { .bLength = sizeof(desc), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = 0x1d50, .idProduct = 0x6089, .bcdDevice = 0x0100, .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, .bNumConfigurations = 1, }; io->length = sizeof(desc); if (ctrl->wLength < io->length) io->length = ctrl->wLength; memcpy(io->data, &desc, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) break; } else if (desc_type == USB_DT_CONFIG) { struct { struct usb_config_descriptor config; struct usb_interface_descriptor interface; } __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 = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 50, }, .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 0, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0xff, .bInterfaceProtocol = 0xff, .iInterface = 0, } }; io->length = sizeof(config_desc); if (ctrl->wLength < io->length) io->length = ctrl->wLength; memcpy(io->data, &config_desc, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) break; } else { if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0) break; } } else if (ctrl->bRequestType == 0xc0) { // USB_DIR_IN | USB_TYPE_VENDOR char dummy_data[256]; memset(dummy_data, 0, sizeof(dummy_data)); if (ctrl->bRequest == 0x0e) { // CMD_BOARD_ID_READ dummy_data[0] = 0x02; // HackRF One io->length = ctrl->wLength; if (io->length > sizeof(dummy_data)) io->length = sizeof(dummy_data); memcpy(io->data, dummy_data, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) break; } else if (ctrl->bRequest == 0x0f) { // CMD_VERSION_STRING_READ // Send 0-length reply to avoid memory allocation in raw-gadget // This ensures failslab targets the kworker thread io->length = 0; enable_failslab(); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) break; // Wait for probe to hit the bug usleep(200000); // 200ms connected = 0; // Break the loop to close fd and disconnect } else { io->length = ctrl->wLength; if (io->length > sizeof(dummy_data)) io->length = sizeof(dummy_data); memcpy(io->data, dummy_data, io->length); if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io) < 0) break; } } else if (ctrl->wLength == 0) { io->length = 0; if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, io) < 0) break; } else { if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0) break; } } } close(fd); disable_failslab(); usleep(100000); // Wait 100ms for disconnect to finish } int main() { setup_failslab_global(); for (int i = 0; i < 100; i++) { run_usb_gadget_once(); } printf("[+] Reproducer finished.\n"); return 0; }