// https://syzkaller.appspot.com/bug?id=11c50242b350e92e4d1e8ab042997a8ed55d1dc1 #define _GNU_SOURCE #include #include #include #include #include #include #include #include void write_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return; } int res = write(fd, val, strlen(val)); if (res < 0) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); } close(fd); } void setup_nvmet() { int res; res = mkdir("/sys/kernel/config/nvmet", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/nvmet: %s\n", strerror(errno)); exit(1); } res = mkdir("/sys/kernel/config/nvmet/subsystems", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/nvmet/subsystems: %s\n", strerror(errno)); exit(1); } res = mkdir("/sys/kernel/config/nvmet/subsystems/testnqn", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/nvmet/subsystems/testnqn: %s\n", strerror(errno)); exit(1); } write_file("/sys/kernel/config/nvmet/subsystems/testnqn/attr_allow_any_host", "1"); res = mkdir("/sys/kernel/config/nvmet/ports", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/nvmet/ports: %s\n", strerror(errno)); exit(1); } res = mkdir("/sys/kernel/config/nvmet/ports/1", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config/nvmet/ports/1: %s\n", strerror(errno)); exit(1); } write_file("/sys/kernel/config/nvmet/ports/1/addr_trtype", "loop"); res = symlink("/sys/kernel/config/nvmet/subsystems/testnqn", "/sys/kernel/config/nvmet/ports/1/subsystems/testnqn"); if (res < 0 && errno != EEXIST) { printf("[-] Failed to symlink: %s\n", strerror(errno)); exit(1); } printf("[+] setup_nvmet successful.\n"); } void setup_fault_injection() { struct stat st; if (stat("/sys/kernel/debug/failslab", &st) == 0) { write_file("/sys/kernel/debug/failslab/probability", "2"); write_file("/sys/kernel/debug/failslab/times", "-1"); write_file("/sys/kernel/debug/failslab/task-filter", "Y"); write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N"); } if (stat("/sys/kernel/debug/fail_page_alloc", &st) == 0) { write_file("/sys/kernel/debug/fail_page_alloc/probability", "2"); write_file("/sys/kernel/debug/fail_page_alloc/times", "-1"); write_file("/sys/kernel/debug/fail_page_alloc/task-filter", "Y"); write_file("/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N"); write_file("/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N"); write_file("/sys/kernel/debug/fail_page_alloc/min-order", "0"); } printf("[+] setup_fault_injection successful.\n"); } int main() { int res = mount("configfs", "/sys/kernel/config", "configfs", 0, NULL); if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); exit(1); } printf("[+] mount configfs successful.\n"); setup_nvmet(); const char *cmd = "transport=loop,nqn=testnqn,nr_io_queues=2,queue_size=128"; // 1. Create a successful connection to initialize the subsystem and hold a refcount. int fd = open("/dev/nvme-fabrics", O_RDWR); if (fd < 0) { printf("[-] Failed to open /dev/nvme-fabrics: %s\n", strerror(errno)); exit(1); } printf("[+] open /dev/nvme-fabrics successful.\n"); ssize_t wres = write(fd, cmd, strlen(cmd)); if (wres < 0) { printf("[-] Failed to write initial connection to /dev/nvme-fabrics: %s\n", strerror(errno)); exit(1); } close(fd); printf("[+] Initial connection successful.\n"); // 2. Setup fault injection. setup_fault_injection(); int fail_fd = open("/proc/self/make-it-fail", O_RDWR); if (fail_fd < 0) { printf("[-] Failed to open /proc/self/make-it-fail: %s\n", strerror(errno)); exit(1); } printf("[+] open /proc/self/make-it-fail successful.\n"); // 3. Try to connect again. We want fault injection to fail an allocation // during blk_mq_alloc_set_map_and_rqs for the second IO queue (i=1). for (int i = 0; i < 2000; i++) { fd = open("/dev/nvme-fabrics", O_RDWR); if (fd < 0) { continue; } // Arm fault injection for the current task using pwrite to avoid allocations pwrite(fail_fd, "1", 1, 0); // Attempt to create the controller write(fd, cmd, strlen(cmd)); // Disarm fault injection using pwrite pwrite(fail_fd, "0", 1, 0); close(fd); } close(fail_fd); printf("[+] Trigger loop completed.\n"); sleep(2); return 0; }