// https://syzkaller.appspot.com/bug?id=773130e459788e5f68da1cfd881b963e4ad87822 #include #include #include #include #include #include #include #include struct virtio_dev { char name[32]; char driver[64]; }; struct virtio_dev target_dev; int dev_found = 0; volatile int stop = 0; // Discovers a single non-critical virtio device (e.g., virtio_rng) void discover_device() { DIR *d = opendir("/sys/bus/virtio/devices"); if (!d) { printf("[-] Failed to open /sys/bus/virtio/devices: %s\n", strerror(errno)); return; } struct dirent *dir; while ((dir = readdir(d)) != NULL) { if (strncmp(dir->d_name, "virtio", 6) == 0) { char path[256]; snprintf(path, sizeof(path), "/sys/bus/virtio/devices/%s/driver", dir->d_name); char link[256]; ssize_t len = readlink(path, link, sizeof(link) - 1); if (len > 0) { link[len] = '\0'; char *driver = strrchr(link, '/'); if (driver) { driver++; // Skip critical devices to avoid breaking the VM if (strcmp(driver, "virtio_blk") == 0 || strcmp(driver, "virtio_console") == 0 || strcmp(driver, "virtio_net") == 0 || strcmp(driver, "virtio_scsi") == 0 || strcmp(driver, "virtio_pci") == 0) continue; strcpy(target_dev.name, dir->d_name); strcpy(target_dev.driver, driver); dev_found = 1; break; } } } } closedir(d); } // Worker thread that repeatedly unbinds and binds the virtio device to allocate/free IRQs void *bind_worker(void *arg) { char unbind_path[256]; char bind_path[256]; snprintf(unbind_path, sizeof(unbind_path), "/sys/bus/virtio/drivers/%s/unbind", target_dev.driver); snprintf(bind_path, sizeof(bind_path), "/sys/bus/virtio/drivers/%s/bind", target_dev.driver); char buf[64]; snprintf(buf, sizeof(buf), "%s\n", target_dev.name); size_t len = strlen(buf); while (!stop) { int fd = open(unbind_path, O_WRONLY); if (fd >= 0) { if (write(fd, buf, len) < 0) { // Ignore write errors, device might be busy or already unbound } close(fd); } fd = open(bind_path, O_WRONLY); if (fd >= 0) { if (write(fd, buf, len) < 0) { // Ignore write errors } close(fd); } } return NULL; } // Worker thread that repeatedly reads procfs to trigger irq_to_desc() lookups void *read_worker(void *arg) { const char *path = (const char *)arg; char buf[4096]; while (!stop) { int fd = open(path, O_RDONLY); if (fd >= 0) { while (read(fd, buf, sizeof(buf)) > 0) {} close(fd); } } return NULL; } int main() { printf("[*] Starting KCSAN data-race reproducer...\n"); discover_device(); if (!dev_found) { printf("[-] No suitable virtio device found. Exiting.\n"); return 1; } printf("[+] Using device %s (driver: %s)\n", target_dev.name, target_dev.driver); pthread_t t1, t2, t3, t4, t5; // Thread 1: repeatedly bind and unbind the virtio device (allocates/frees IRQs) if (pthread_create(&t1, NULL, bind_worker, NULL) != 0) { printf("[-] Failed to create bind_worker thread\n"); return 1; } // Threads 2-5: repeatedly read /proc/stat and /proc/interrupts (looks up IRQs) pthread_create(&t2, NULL, read_worker, "/proc/stat"); pthread_create(&t3, NULL, read_worker, "/proc/interrupts"); pthread_create(&t4, NULL, read_worker, "/proc/stat"); pthread_create(&t5, NULL, read_worker, "/proc/interrupts"); // Run for 10 seconds to trigger the race sleep(10); stop = 1; pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); pthread_join(t5, NULL); printf("[+] Done.\n"); return 0; }