// https://syzkaller.appspot.com/bug?id=11c50242b350e92e4d1e8ab042997a8ed55d1dc1 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include int write_file_ret(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { return -1; } int ret = write(fd, val, strlen(val)); close(fd); return ret; } void write_file(const char *path, const char *val, int ignore_err) { if (write_file_ret(path, val) < 0) { if (!ignore_err) { printf("[-] Failed to write '%s' to %s: %s\n", val, path, strerror(errno)); exit(1); } } } void delete_controllers() { DIR *d = opendir("/sys/class/nvme"); if (!d) return; struct dirent *dir; while ((dir = readdir(d)) != NULL) { if (strncmp(dir->d_name, "nvme", 4) == 0) { char path[256]; snprintf(path, sizeof(path), "/sys/class/nvme/%s/delete_controller", dir->d_name); write_file_ret(path, "1\n"); } } closedir(d); } void setup_fcloop() { write_file("/sys/devices/virtual/fcloop/ctl/add_local_port", "wwnn=0x0000000000000001,wwpn=0x0000000000000001,roles=48\n", 0); write_file("/sys/devices/virtual/fcloop/ctl/add_target_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002\n", 0); write_file("/sys/devices/virtual/fcloop/ctl/add_remote_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002,lpwwnn=0x0000000000000001,lpwwpn=0x0000000000000001,roles=48\n", 0); if (mkdir("/sys/kernel/config/nvmet/subsystems/test", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir subsystem: %s\n", strerror(errno)); exit(1); } write_file("/sys/kernel/config/nvmet/subsystems/test/attr_allow_any_host", "1\n", 0); if (mkdir("/sys/kernel/config/nvmet/ports/1", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir port: %s\n", strerror(errno)); exit(1); } write_file("/sys/kernel/config/nvmet/ports/1/addr_trtype", "fc\n", 0); write_file("/sys/kernel/config/nvmet/ports/1/addr_adrfam", "fc\n", 0); write_file("/sys/kernel/config/nvmet/ports/1/addr_traddr", "nn-0x0000000000000002:pn-0x0000000000000002\n", 0); if (symlink("/sys/kernel/config/nvmet/subsystems/test", "/sys/kernel/config/nvmet/ports/1/subsystems/test") < 0 && errno != EEXIST) { printf("[-] Failed to symlink: %s\n", strerror(errno)); exit(1); } } void teardown_fcloop() { write_file("/sys/devices/virtual/fcloop/ctl/del_remote_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002\n", 1); write_file("/sys/devices/virtual/fcloop/ctl/del_target_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002\n", 1); write_file("/sys/devices/virtual/fcloop/ctl/del_local_port", "wwnn=0x0000000000000001,wwpn=0x0000000000000001\n", 1); unlink("/sys/kernel/config/nvmet/ports/1/subsystems/test"); rmdir("/sys/kernel/config/nvmet/ports/1"); rmdir("/sys/kernel/config/nvmet/subsystems/test"); delete_controllers(); } int main() { printf("[+] Starting fcloop UAF reproducer...\n"); for (int i = 0; i < 500; i++) { if (i % 50 == 0) printf("[*] Iteration %d\n", i); teardown_fcloop(); setup_fcloop(); usleep(50000); // Wait for setup // Connect host if (write_file_ret("/dev/nvme-fabrics", "nqn=test,transport=fc,traddr=nn-0x0000000000000002:pn-0x0000000000000002,host_traddr=nn-0x0000000000000001:pn-0x0000000000000001,hostnqn=nqn.2014-08.org.nvmexpress:uuid:41c2cfa0-1658-4591-925a-27791f143716,hostid=41c2cfa0-1658-4591-925a-27791f143716\n") < 0) { printf("[-] Failed to connect host: %s\n", strerror(errno)); teardown_fcloop(); continue; } usleep(50000); // Wait for connection to establish // 1. Trigger disconnect LS from target to host unlink("/sys/kernel/config/nvmet/ports/1/subsystems/test"); // 2. Wait for the LS request to be dispatched to the host. // Varying the delay to increase the chance of hitting the race window. int delay = (i % 50) * 100; usleep(delay); // 3. Delete remote port first to clear nport->rport write_file("/sys/devices/virtual/fcloop/ctl/del_remote_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002\n", 1); // 4. Delete target port. Since nport->rport is NULL, rport->targetport is left dangling. // This also frees the pending lsreq. write_file("/sys/devices/virtual/fcloop/ctl/del_target_port", "wwnn=0x0000000000000002,wwpn=0x0000000000000002\n", 1); usleep(50000); // Allow host workqueue to process the LS and hit the UAF } teardown_fcloop(); printf("[+] Done.\n"); return 0; }