// https://syzkaller.appspot.com/bug?id=5dc9c6841a009135aa85b3769624d99420626ec2 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include int setup_loop(int *loop_num) { int loop_ctrl = open("/dev/loop-control", O_RDWR); if (loop_ctrl < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); return -1; } *loop_num = ioctl(loop_ctrl, LOOP_CTL_GET_FREE); close(loop_ctrl); if (*loop_num < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); return -1; } char temp_path[] = "/tmp/backingXXXXXX"; int fd = mkstemp(temp_path); if (fd < 0) { printf("[-] Failed to create temp file: %s\n", strerror(errno)); return -1; } if (ftruncate(fd, 10 * 1024 * 1024) < 0) { printf("[-] Failed to truncate temp file: %s\n", strerror(errno)); close(fd); return -1; } unlink(temp_path); char loop_path[32]; snprintf(loop_path, sizeof(loop_path), "/dev/loop%d", *loop_num); mknod(loop_path, S_IFBLK | 0600, makedev(7, *loop_num)); int loop_fd = open(loop_path, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", loop_path, strerror(errno)); close(fd); return -1; } if (ioctl(loop_fd, LOOP_SET_FD, fd) < 0) { printf("[-] Failed to LOOP_SET_FD on %s: %s\n", loop_path, strerror(errno)); close(loop_fd); close(fd); return -1; } close(fd); printf("[+] Loop device %d setup successfully.\n", *loop_num); return loop_fd; } int main(void) { int loop_num1, loop_num2; int loop1_fd = setup_loop(&loop_num1); if (loop1_fd < 0) exit(1); int loop2_fd = setup_loop(&loop_num2); if (loop2_fd < 0) exit(1); int minor = 127; char dev_name[32]; snprintf(dev_name, sizeof(dev_name), "/dev/md%d", minor); // 1. Record: major_names_lock -> (wq_completion)md_misc // Open a non-existent md device to trigger md_probe -> md_alloc -> flush_workqueue unlink(dev_name); if (mknod(dev_name, S_IFBLK | 0600, makedev(9, minor)) < 0) { printf("[-] Failed to mknod %s: %s\n", dev_name, strerror(errno)); exit(1); } int md_fd = open(dev_name, O_RDWR); if (md_fd < 0) { printf("[-] Failed to open %s: %s\n", dev_name, strerror(errno)); exit(1); } printf("[+] %s opened successfully.\n", dev_name); // 2. Record: mddev->reconfig_mutex -> major_names_lock // ADD_NEW_DISK with a non-existent device (major 0, minor 0) mdu_disk_info_t bad_disk = {0}; bad_disk.major = 0; bad_disk.minor = 0; if (ioctl(md_fd, ADD_NEW_DISK, &bad_disk) < 0) { printf("[+] ADD_NEW_DISK with bad disk failed as expected: %s\n", strerror(errno)); } else { printf("[+] ADD_NEW_DISK with bad disk succeeded (unexpected).\n"); } // 3. Configure the array and add real disks mdu_array_info_t array_info = {0}; array_info.level = 1; array_info.raid_disks = 2; array_info.not_persistent = 1; if (ioctl(md_fd, SET_ARRAY_INFO, &array_info) < 0) { printf("[-] Failed to SET_ARRAY_INFO: %s\n", strerror(errno)); exit(1); } printf("[+] SET_ARRAY_INFO successful.\n"); mdu_disk_info_t disk1 = { .number = 0, .major = 7, .minor = loop_num1, .raid_disk = 0, .state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC) }; if (ioctl(md_fd, ADD_NEW_DISK, &disk1) < 0) { printf("[-] Failed to ADD_NEW_DISK 1: %s\n", strerror(errno)); exit(1); } printf("[+] ADD_NEW_DISK 1 successful.\n"); mdu_disk_info_t disk2 = { .number = 1, .major = 7, .minor = loop_num2, .raid_disk = 1, .state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC) }; if (ioctl(md_fd, ADD_NEW_DISK, &disk2) < 0) { printf("[-] Failed to ADD_NEW_DISK 2: %s\n", strerror(errno)); exit(1); } printf("[+] ADD_NEW_DISK 2 successful.\n"); if (ioctl(md_fd, RUN_ARRAY, 0) < 0) { printf("[-] Failed to RUN_ARRAY: %s\n", strerror(errno)); exit(1); } printf("[+] RUN_ARRAY successful.\n"); // 4. Record: (wq_completion)md_misc -> (work_completion)(&mddev->sync_work) -> reconfig_mutex // Trigger a sync check which queues sync_work. The worker thread executes md_start_sync // and completes the circular dependency graph in lockdep. char sync_path[64]; snprintf(sync_path, sizeof(sync_path), "/sys/block/md%d/md/sync_action", minor); int sync_fd = open(sync_path, O_WRONLY); if (sync_fd >= 0) { if (write(sync_fd, "check\n", 6) < 0) { printf("[-] Failed to write sync_action: %s\n", strerror(errno)); } else { printf("[+] sync_action triggered successfully.\n"); } close(sync_fd); } else { printf("[-] Failed to open sync_action: %s\n", strerror(errno)); } // Wait for the worker thread to execute md_start_sync and trigger the lockdep splat sleep(2); // Cleanup ioctl(md_fd, STOP_ARRAY, 0); close(md_fd); if (loop1_fd >= 0) { ioctl(loop1_fd, LOOP_CLR_FD, 0); close(loop1_fd); } if (loop2_fd >= 0) { ioctl(loop2_fd, LOOP_CLR_FD, 0); close(loop2_fd); } printf("[+] Done.\n"); return 0; }