// https://syzkaller.appspot.com/bug?id=4e213b401acbc41bebcc4d00408444942006c069 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SYS_memfd_create #define SYS_memfd_create 319 #endif int main() { int loop_fds[3]; void *buf; // O_DIRECT requires aligned memory if (posix_memalign(&buf, 4096, 4096) != 0) { printf("[-] Failed to posix_memalign: %s\n", strerror(errno)); exit(1); } printf("[+] posix_memalign successful.\n"); // 1. Create 3 loop devices backed by memfd for (int i = 0; i < 3; i++) { int memfd = syscall(SYS_memfd_create, "disk", 0); if (memfd < 0) { printf("[-] Failed to memfd_create: %s\n", strerror(errno)); exit(1); } if (ftruncate(memfd, 1024 * 1024 * 10) < 0) { printf("[-] Failed to ftruncate: %s\n", strerror(errno)); exit(1); } int ctl = open("/dev/loop-control", O_RDWR); if (ctl < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } int loop_idx = ioctl(ctl, LOOP_CTL_GET_FREE); if (loop_idx < 0) { printf("[-] Failed to ioctl LOOP_CTL_GET_FREE: %s\n", strerror(errno)); exit(1); } close(ctl); char path[32]; sprintf(path, "/dev/loop%d", loop_idx); loop_fds[i] = open(path, O_RDWR); if (loop_fds[i] < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } if (ioctl(loop_fds[i], LOOP_SET_FD, memfd) < 0) { printf("[-] Failed to ioctl LOOP_SET_FD: %s\n", strerror(errno)); exit(1); } printf("[+] Loop device %s created successfully.\n", path); } // 2. Create md0 node if (mknod("/dev/md0", S_IFBLK | 0600, makedev(MD_MAJOR, 0)) < 0 && errno != EEXIST) { printf("[-] Failed to mknod /dev/md0: %s\n", strerror(errno)); exit(1); } printf("[+] mknod /dev/md0 successful.\n"); // Shared memory for synchronizing the race volatile int *sync_start = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (sync_start == MAP_FAILED) { printf("[-] Failed to mmap: %s\n", strerror(errno)); exit(1); } printf("[+] mmap successful.\n"); // 3. Loop to hit the race condition for (int iter = 0; iter < 1000; iter++) { int md_fd = open("/dev/md0", O_RDWR | O_DIRECT); if (md_fd < 0) { printf("[-] Failed to open /dev/md0: %s\n", strerror(errno)); continue; } // Configure as RAID5 with 3 disks mdu_array_info_t array = { .level = 5, .size = 10240, .nr_disks = 3, .raid_disks = 3, .md_minor = 0, .not_persistent = 1, .state = (1 << MD_SB_CLEAN), .active_disks = 3, .working_disks = 3, .failed_disks = 0, .spare_disks = 0, .layout = 2, // ALGORITHM_LEFT_SYMMETRIC .chunk_size = 4096, }; if (ioctl(md_fd, SET_ARRAY_INFO, &array) < 0) { printf("[-] Failed to ioctl SET_ARRAY_INFO: %s\n", strerror(errno)); close(md_fd); continue; } // Add all 3 disks so raid5_run succeeds int add_failed = 0; for (int i = 0; i < 3; i++) { struct stat st; if (fstat(loop_fds[i], &st) < 0) { printf("[-] Failed to fstat loop%d: %s\n", i, strerror(errno)); add_failed = 1; break; } mdu_disk_info_t disk = { .number = i, .raid_disk = i, .state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC), .major = major(st.st_rdev), .minor = minor(st.st_rdev), }; if (ioctl(md_fd, ADD_NEW_DISK, &disk) < 0) { printf("[-] Failed to ioctl ADD_NEW_DISK %d: %s\n", i, strerror(errno)); add_failed = 1; break; } } if (add_failed) { close(md_fd); continue; } if (ioctl(md_fd, RUN_ARRAY, 0) < 0) { printf("[-] Failed to ioctl RUN_ARRAY: %s\n", strerror(errno)); close(md_fd); continue; } *sync_start = 0; // Spawn multiple readers to maximize the race window. // fork() duplicates the fd but does not increment bdev openers, // allowing STOP_ARRAY to proceed concurrently. pid_t pids[4]; for (int i = 0; i < 4; i++) { pids[i] = fork(); if (pids[i] < 0) { printf("[-] Failed to fork: %s\n", strerror(errno)); exit(1); } if (pids[i] == 0) { while (*sync_start == 0); // Spin wait if (pread(md_fd, buf, 4096, 0) < 0) { // Ignore errors in children } exit(0); } } usleep(1000); // Wait for children to reach the spin loop *sync_start = 1; // GO! // Race STOP_ARRAY against the preads ioctl(md_fd, STOP_ARRAY, 0); usleep(10000); // Wait 10ms for the race to resolve // Clean up children (kill them if they got stuck due to the array stopping) for (int i = 0; i < 4; i++) { kill(pids[i], SIGKILL); waitpid(pids[i], NULL, 0); } close(md_fd); if (iter % 100 == 0) { printf("[+] Iteration %d successful.\n", iter); } } printf("[+] Race loop finished.\n"); return 0; }