// https://syzkaller.appspot.com/bug?id=186780e3274fd3e622f628a8283f5ff93518a8ee #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include int loop_major[2]; int loop_minor[2]; void write_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd >= 0) { if (write(fd, val, strlen(val)) < 0) {} close(fd); } } int setup_loop(int loop_num, const char *file) { int file_fd = open(file, O_RDWR | O_CREAT, 0644); if (file_fd < 0) { printf("[-] Failed to open %s: %s\n", file, strerror(errno)); exit(1); } if (ftruncate(file_fd, 100 * 1024 * 1024) < 0) { printf("[-] Failed to ftruncate %s: %s\n", file, strerror(errno)); exit(1); } char loop_name[64]; snprintf(loop_name, sizeof(loop_name), "/dev/loop%d", loop_num); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { if (mknod(loop_name, S_IFBLK | 0644, makedev(7, loop_num)) < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", loop_name, strerror(errno)); } loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", loop_name, strerror(errno)); exit(1); } } struct stat st; if (fstat(loop_fd, &st) == 0) { loop_major[loop_num] = major(st.st_rdev); loop_minor[loop_num] = minor(st.st_rdev); } else { loop_major[loop_num] = 7; loop_minor[loop_num] = loop_num; } ioctl(loop_fd, LOOP_CLR_FD, 0); if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { printf("[-] Failed to LOOP_SET_FD %s: %s\n", loop_name, strerror(errno)); exit(1); } close(loop_fd); close(file_fd); printf("[+] Setup loop%d successful.\n", loop_num); return 0; } int create_raid1(int md_idx) { char dev_name[32]; char sys_name[32]; char sys_path[128]; snprintf(dev_name, sizeof(dev_name), "/dev/md%d", md_idx); snprintf(sys_name, sizeof(sys_name), "md%d", md_idx); int fd = open(dev_name, O_RDWR); if (fd < 0) { if (mknod(dev_name, S_IFBLK | 0644, makedev(9, md_idx)) < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", dev_name, strerror(errno)); } fd = open(dev_name, O_RDWR); if (fd < 0) { char buf[64]; snprintf(buf, sizeof(buf), "%s\n", sys_name); int sys_fd = open("/sys/module/md_mod/parameters/new_array", O_WRONLY); if (sys_fd >= 0) { if (write(sys_fd, buf, strlen(buf)) < 0 && errno != EEXIST) { printf("[-] Failed to write new_array: %s\n", strerror(errno)); } close(sys_fd); } fd = open(dev_name, O_RDWR); if (fd < 0) { printf("[-] Failed to open %s: %s\n", dev_name, strerror(errno)); return -1; } } } snprintf(sys_path, sizeof(sys_path), "/sys/block/%s/md/sync_action", sys_name); int sync_fd = open(sys_path, O_WRONLY); if (sync_fd >= 0) { if (write(sync_fd, "idle", 4) < 0) {} close(sync_fd); } int retries = 10; while (ioctl(fd, STOP_ARRAY, 0) < 0 && errno == EBUSY && retries-- > 0) { usleep(10000); } mdu_array_info_t array; memset(&array, 0, sizeof(array)); array.level = 1; array.size = 10000; array.nr_disks = 2; array.raid_disks = 2; array.md_minor = md_idx; array.not_persistent = 1; array.state = 0; array.active_disks = 2; array.working_disks = 2; if (ioctl(fd, SET_ARRAY_INFO, &array) < 0) { printf("[-] Failed to SET_ARRAY_INFO for %s: %s\n", dev_name, strerror(errno)); close(fd); return -1; } mdu_disk_info_t disk; memset(&disk, 0, sizeof(disk)); disk.number = 0; disk.raid_disk = 0; disk.state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC); disk.major = loop_major[0]; disk.minor = loop_minor[0]; if (ioctl(fd, ADD_NEW_DISK, &disk) < 0) { printf("[-] Failed to ADD_NEW_DISK 0 for %s: %s\n", dev_name, strerror(errno)); close(fd); return -1; } memset(&disk, 0, sizeof(disk)); disk.number = 1; disk.raid_disk = 1; disk.state = (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC); disk.major = loop_major[1]; disk.minor = loop_minor[1]; if (ioctl(fd, ADD_NEW_DISK, &disk) < 0) { printf("[-] Failed to ADD_NEW_DISK 1 for %s: %s\n", dev_name, strerror(errno)); close(fd); return -1; } if (ioctl(fd, RUN_ARRAY, &array) < 0) { printf("[-] Failed to RUN_ARRAY for %s: %s\n", dev_name, strerror(errno)); close(fd); return -1; } close(fd); sync_fd = open(sys_path, O_RDWR); if (sync_fd >= 0) { if (write(sync_fd, "idle", 4) < 0) {} char buf[16]; for (int i = 0; i < 100; i++) { lseek(sync_fd, 0, SEEK_SET); int n = read(sync_fd, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = 0; if (strncmp(buf, "idle", 4) == 0) break; } usleep(10000); } close(sync_fd); } printf("[+] create_raid1(%d) successful.\n", md_idx); return 0; } void configure_fail_nth(int n) { char path[128]; char buf[32]; snprintf(path, sizeof(path), "/proc/self/task/%ld/fail-nth", syscall(SYS_gettid)); int fd = open(path, O_WRONLY); if (fd >= 0) { snprintf(buf, sizeof(buf), "%d", n); if (write(fd, buf, strlen(buf)) < 0) {} close(fd); } } void disable_fail_nth() { configure_fail_nth(0); } int main() { setup_loop(0, "/tmp/disk0.img"); setup_loop(1, "/tmp/disk1.img"); /* Enable failslab for GFP_KERNEL allocations */ write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "0"); int md_idx = 0; /* Load raid5 module by doing a successful takeover first */ if (create_raid1(md_idx) == 0) { char sys_path[128]; snprintf(sys_path, sizeof(sys_path), "/sys/block/md%d/md/level", md_idx); int sys_fd = open(sys_path, O_WRONLY); if (sys_fd >= 0) { if (write(sys_fd, "raid5", 5) < 0) {} close(sys_fd); } } char dev_name[32]; snprintf(dev_name, sizeof(dev_name), "/dev/md%d", md_idx); int fd = open(dev_name, O_RDWR); if (fd >= 0) { int retries = 50; while (ioctl(fd, STOP_ARRAY, 0) < 0 && errno == EBUSY && retries-- > 0) { usleep(20000); } close(fd); } md_idx++; /* Sweep fail-nth backwards from 200 down to 30 to hit grow_stripes first! * Sweeping backwards ensures we hit the allocations inside grow_stripes * before hitting the early allocations in setup_conf that cause KASAN. */ for (int n = 200; n >= 30; n -= 5) { printf("[+] Testing fail-nth = %d\n", n); if (create_raid1(md_idx) < 0) { md_idx++; continue; } char sys_path[128]; snprintf(sys_path, sizeof(sys_path), "/sys/block/md%d/md/level", md_idx); int sys_fd = open(sys_path, O_WRONLY); if (sys_fd >= 0) { configure_fail_nth(n); if (write(sys_fd, "raid5", 5) < 0) { // Expected to fail when fail-nth triggers } disable_fail_nth(); close(sys_fd); } snprintf(dev_name, sizeof(dev_name), "/dev/md%d", md_idx); fd = open(dev_name, O_RDWR); if (fd >= 0) { int retries = 50; while (ioctl(fd, STOP_ARRAY, 0) < 0 && errno == EBUSY && retries-- > 0) { usleep(20000); } close(fd); } md_idx++; /* Sleep a bit to allow WARNING to be processed and panic if panic_on_warn is set */ usleep(100000); } printf("[+] Done sweeping fail-nth.\n"); sleep(2); return 0; }