// https://syzkaller.appspot.com/bug?id=730c789cf47674e5d2c20e08507da1f47ea8c719 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #ifndef MD_MAJOR #define MD_MAJOR 9 #endif #include static void write_sysfs_param(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd >= 0) { ssize_t len = strlen(val); ssize_t written = write(fd, val, len); if (written == len) { printf("[+] Successfully wrote '%s' to %s\n", val, path); } else { printf("[-] Failed to write '%s' to %s: %s\n", val, path, strerror(errno)); } close(fd); } else { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); } } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for circular locking dependency in md_alloc...\n"); /* Ensure create_on_open is enabled so that md_probe calls md_alloc_and_put */ write_sysfs_param("/sys/module/md_mod/parameters/create_on_open", "1\n"); write_sysfs_param("/sys/module/md/parameters/create_on_open", "1\n"); /* Step 1: Establish disks_mutex -> mddev->reconfig_mutex */ int sysfs_fd = open("/sys/module/md_mod/parameters/new_array", O_WRONLY); if (sysfs_fd < 0) { sysfs_fd = open("/sys/module/md/parameters/new_array", O_WRONLY); } if (sysfs_fd < 0) { printf("[-] Failed to open new_array sysfs parameter: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully opened new_array sysfs parameter.\n"); int unit = -1; char array_name[32]; for (int i = 0; i < 16; i++) { snprintf(array_name, sizeof(array_name), "md%d\n", i); ssize_t written = write(sysfs_fd, array_name, strlen(array_name)); if (written > 0) { unit = i; printf("[+] Successfully created md%d via new_array write.\n", unit); break; } } int res = close(sysfs_fd); if (res < 0) { printf("[-] Failed to close sysfs_fd: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully closed sysfs_fd.\n"); if (unit < 0) { printf("[-] Could not create any md device via new_array\n"); exit(1); } /* Step 2: Establish mddev->reconfig_mutex -> major_names_lock */ char md_node_path[64]; snprintf(md_node_path, sizeof(md_node_path), "/tmp/repro_md%d", unit); unlink(md_node_path); res = mknod(md_node_path, S_IFBLK | 0660, makedev(MD_MAJOR, unit)); if (res < 0) { printf("[-] Failed to mknod %s: %s\n", md_node_path, strerror(errno)); exit(1); } printf("[+] Successfully created block device node %s.\n", md_node_path); int md_fd = open(md_node_path, O_RDWR); if (md_fd < 0) { printf("[-] Failed to open %s: %s\n", md_node_path, strerror(errno)); exit(1); } printf("[+] Successfully opened %s.\n", md_node_path); mdu_array_info_t ainfo; memset(&ainfo, 0, sizeof(ainfo)); ainfo.raid_disks = 1; ainfo.level = 0; ainfo.size = 0; res = ioctl(md_fd, SET_ARRAY_INFO, &ainfo); if (res < 0) { printf("[-] Failed to ioctl SET_ARRAY_INFO: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully executed SET_ARRAY_INFO ioctl.\n"); /* * Use unallocated minors (e.g. 200, 250) on major 8 (sd). * ilookup() returns NULL, triggering blk_request_module() -> blk_probe_dev() * which acquires major_names_lock while mddev->reconfig_mutex is held. * ioctl is expected to return -1 with ENXIO, establishing the lock dependency. */ mdu_disk_info_t dinfo; memset(&dinfo, 0, sizeof(dinfo)); dinfo.major = 8; dinfo.minor = 200; res = ioctl(md_fd, ADD_NEW_DISK, &dinfo); if (res < 0) { printf("[+] ioctl ADD_NEW_DISK (major %d, minor %d) returned %d (errno: %s) as expected for unallocated device.\n", dinfo.major, dinfo.minor, res, strerror(errno)); } else { printf("[+] ioctl ADD_NEW_DISK succeeded.\n"); } dinfo.minor = 250; res = ioctl(md_fd, ADD_NEW_DISK, &dinfo); if (res < 0) { printf("[+] ioctl ADD_NEW_DISK (major %d, minor %d) returned %d (errno: %s).\n", dinfo.major, dinfo.minor, res, strerror(errno)); } else { printf("[+] ioctl ADD_NEW_DISK succeeded.\n"); } res = close(md_fd); if (res < 0) { printf("[-] Failed to close md_fd: %s\n", strerror(errno)); exit(1); } printf("[+] Successfully closed %s.\n", md_node_path); /* Step 3: Trigger major_names_lock -> disks_mutex */ int probe_minor = (unit == 9) ? 10 : 9; char probe_node_path[64]; snprintf(probe_node_path, sizeof(probe_node_path), "/tmp/repro_probe_md%d", probe_minor); unlink(probe_node_path); res = mknod(probe_node_path, S_IFBLK | 0660, makedev(MD_MAJOR, probe_minor)); if (res < 0) { printf("[-] Failed to mknod %s: %s\n", probe_node_path, strerror(errno)); exit(1); } printf("[+] Successfully created probe device node %s (major %d, minor %d).\n", probe_node_path, MD_MAJOR, probe_minor); int probe_fd = open(probe_node_path, O_RDWR); if (probe_fd < 0) { printf("[*] open %s returned %d (%s)\n", probe_node_path, probe_fd, strerror(errno)); } else { printf("[+] open %s succeeded (fd=%d), md_probe completed.\n", probe_node_path, probe_fd); close(probe_fd); } /* Fallback probe minor 11 if minor 9/10 was already probed */ int probe_minor2 = (probe_minor == 9) ? 11 : 12; char probe_node_path2[64]; snprintf(probe_node_path2, sizeof(probe_node_path2), "/tmp/repro_probe_md%d", probe_minor2); unlink(probe_node_path2); res = mknod(probe_node_path2, S_IFBLK | 0660, makedev(MD_MAJOR, probe_minor2)); if (res == 0) { int probe_fd2 = open(probe_node_path2, O_RDWR); if (probe_fd2 >= 0) { printf("[+] open %s succeeded (fd=%d).\n", probe_node_path2, probe_fd2); close(probe_fd2); } else { printf("[*] open %s returned %d (%s)\n", probe_node_path2, probe_fd2, strerror(errno)); } unlink(probe_node_path2); } res = unlink(md_node_path); if (res < 0) { printf("[-] Failed to unlink %s: %s\n", md_node_path, strerror(errno)); exit(1); } printf("[+] Successfully unlinked %s.\n", md_node_path); res = unlink(probe_node_path); if (res < 0) { printf("[-] Failed to unlink %s: %s\n", probe_node_path, strerror(errno)); exit(1); } printf("[+] Successfully unlinked %s.\n", probe_node_path); sleep(1); printf("[+] Reproducer completed successfully.\n"); return 0; }