// https://syzkaller.appspot.com/bug?id=af5f1e698421629afe678fafb2eb6bfd743845db #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define MD_SB_MAGIC 0xa92b4efc #define MD_SB_BYTES 4096 typedef struct { uint32_t number; uint32_t major; uint32_t minor; uint32_t raid_disk; uint32_t state; uint32_t reserved[27]; } mdp_disk_t; typedef struct { uint32_t md_magic; uint32_t major_version; uint32_t minor_version; uint32_t patch_version; uint32_t gvalid_words; uint32_t set_uuid0; uint32_t ctime; uint32_t level; uint32_t size; uint32_t nr_disks; uint32_t raid_disks; uint32_t md_minor; uint32_t not_persistent; uint32_t set_uuid1; uint32_t set_uuid2; uint32_t set_uuid3; uint32_t gstate_creserved[16]; uint32_t utime; uint32_t state; uint32_t active_disks; uint32_t working_disks; uint32_t failed_disks; uint32_t spare_disks; uint32_t sb_csum; uint32_t events_lo; uint32_t events_hi; uint32_t cp_events_lo; uint32_t cp_events_hi; uint32_t recovery_cp; uint64_t reshape_position; uint32_t new_level; uint32_t delta_disks; uint32_t new_layout; uint32_t new_chunk; uint32_t gstate_sreserved[14]; uint32_t layout; uint32_t chunk_size; uint32_t root_pv; uint32_t root_block; uint32_t pstate_reserved[60]; mdp_disk_t disks[27]; mdp_disk_t this_disk; } mdp_super_t; static uint32_t calc_sb_csum(mdp_super_t *sb) { uint64_t newcsum = 0; uint32_t *sb32 = (uint32_t *)sb; uint32_t disk_csum = sb->sb_csum; sb->sb_csum = 0; for (int i = 0; i < MD_SB_BYTES / 4; i++) newcsum += sb32[i]; uint32_t csum = (newcsum & 0xffffffff) + (newcsum >> 32); sb->sb_csum = disk_csum; return csum; } static 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) { if (errno != EEXIST) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); exit(1); } } close(fd); printf("[+] Wrote '%s' to %s\n", val, path); } else { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } } static void write_file_no_exit(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd >= 0) { if (write(fd, val, strlen(val)) < 0) { // Ignore errors during cleanup } close(fd); } } int main() { // 1. Create a 2MB backing file int fd = open("md_backing.img", O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { printf("[-] Failed to open backing file: %s\n", strerror(errno)); exit(1); } if (ftruncate(fd, 2 * 1024 * 1024) < 0) { printf("[-] Failed to ftruncate backing file: %s\n", strerror(errno)); exit(1); } printf("[+] Created backing file.\n"); // 2. Setup loop device int loop_ctl = open("/dev/loop-control", O_RDWR); if (loop_ctl < 0) { printf("[-] Failed to open loop-control: %s\n", strerror(errno)); exit(1); } int loop_num = ioctl(loop_ctl, LOOP_CTL_GET_FREE); if (loop_num < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); exit(1); } close(loop_ctl); char loop_path[64]; snprintf(loop_path, sizeof(loop_path), "/dev/loop%d", loop_num); 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)); exit(1); } if (ioctl(loop_fd, LOOP_SET_FD, fd) < 0) { printf("[-] Failed to set loop fd: %s\n", strerror(errno)); exit(1); } printf("[+] Setup loop device %s.\n", loop_path); // 3. Write 0.90 Superblock mdp_super_t sb; memset(&sb, 0, sizeof(sb)); sb.md_magic = MD_SB_MAGIC; sb.major_version = 0; sb.minor_version = 90; sb.raid_disks = 1; sb.level = 1; sb.size = 1000; sb.this_disk.number = 0; sb.disks[0].state = 6; // MD_DISK_ACTIVE | MD_DISK_SYNC sb.sb_csum = calc_sb_csum(&sb); // Offset for 2MB file: (4096 & ~127) - 128 = 3968 sectors = 2031616 bytes if (lseek(fd, 2031616, SEEK_SET) < 0) { printf("[-] Failed to lseek: %s\n", strerror(errno)); exit(1); } if (write(fd, &sb, sizeof(sb)) != sizeof(sb)) { printf("[-] Failed to write superblock: %s\n", strerror(errno)); exit(1); } close(fd); printf("[+] Wrote 0.90 superblock.\n"); // 4. Create md array write_file("/sys/module/md_mod/parameters/new_array", "md_ubsan_test"); // 5. Configure array write_file("/sys/block/md_ubsan_test/md/metadata_version", "0.90"); write_file("/sys/block/md_ubsan_test/md/raid_disks", "124"); // 6. Import loop device struct stat st; if (fstat(loop_fd, &st) < 0) { printf("[-] Failed to fstat loop device: %s\n", strerror(errno)); exit(1); } char dev_str[32]; snprintf(dev_str, sizeof(dev_str), "%d:%d", major(st.st_rdev), minor(st.st_rdev)); write_file("/sys/block/md_ubsan_test/md/new_dev", dev_str); // 7. Trigger super_90_sync OOB char state_path[128]; snprintf(state_path, sizeof(state_path), "/sys/block/md_ubsan_test/md/dev-loop%d/state", loop_num); write_file(state_path, "failfast"); // Give it a moment to trigger the bug sleep(1); // Cleanup write_file_no_exit(state_path, "remove"); write_file_no_exit("/sys/block/md_ubsan_test/md/array_state", "clear"); if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { printf("[-] Failed to clear loop fd: %s\n", strerror(errno)); } close(loop_fd); unlink("md_backing.img"); printf("[+] Done.\n"); return 0; }