// autogenerated by syzkaller (https://github.com/google/syzkaller) #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void sleep_ms(uint64_t ms) { usleep(ms * 1000); } static uint64_t current_time_ms(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)) exit(1); return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; } static void thread_start(void* (*fn)(void*), void* arg) { pthread_t th; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 128 << 10); int i = 0; for (; i < 100; i++) { if (pthread_create(&th, &attr, fn, arg) == 0) { pthread_attr_destroy(&attr); return; } if (errno == EAGAIN) { usleep(50); continue; } break; } exit(1); } typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (ev->state) exit(1); __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } static int event_isset(event_t* ev) { return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); } static int event_timedwait(event_t* ev, uint64_t timeout) { uint64_t start = current_time_ms(); uint64_t now = start; for (;;) { uint64_t remain = timeout - (now - start); struct timespec ts; ts.tv_sec = remain / 1000; ts.tv_nsec = (remain % 1000) * 1000 * 1000; syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) return 1; now = current_time_ms(); if (now - start > timeout) return 0; } } static bool write_file(const char* file, const char* what, ...) { char buf[1024]; va_list args; va_start(args, what); vsnprintf(buf, sizeof(buf), what, args); va_end(args); buf[sizeof(buf) - 1] = 0; int len = strlen(buf); int fd = open(file, O_WRONLY | O_CLOEXEC); if (fd == -1) return false; if (write(fd, buf, len) != len) { int err = errno; close(fd); errno = err; return false; } close(fd); return true; } static void setup_gadgetfs(); static void setup_binderfs(); static void setup_fusectl(); static void sandbox_common_mount_tmpfs(void) { write_file("/proc/sys/fs/mount-max", "100000"); if (mkdir("./syz-tmp", 0777)) exit(1); if (mount("", "./syz-tmp", "tmpfs", 0, NULL)) exit(1); if (mkdir("./syz-tmp/newroot", 0777)) exit(1); if (mkdir("./syz-tmp/newroot/dev", 0700)) exit(1); unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; if (mount("/dev", "./syz-tmp/newroot/dev", NULL, bind_mount_flags, NULL)) exit(1); if (mkdir("./syz-tmp/newroot/proc", 0700)) exit(1); if (mount("syz-proc", "./syz-tmp/newroot/proc", "proc", 0, NULL)) exit(1); if (mkdir("./syz-tmp/newroot/selinux", 0700)) exit(1); const char* selinux_path = "./syz-tmp/newroot/selinux"; if (mount("/selinux", selinux_path, NULL, bind_mount_flags, NULL)) { if (errno != ENOENT) exit(1); if (mount("/sys/fs/selinux", selinux_path, NULL, bind_mount_flags, NULL) && errno != ENOENT) exit(1); } if (mkdir("./syz-tmp/newroot/sys", 0700)) exit(1); if (mount("/sys", "./syz-tmp/newroot/sys", 0, bind_mount_flags, NULL)) exit(1); if (mount("/sys/kernel/debug", "./syz-tmp/newroot/sys/kernel/debug", NULL, bind_mount_flags, NULL) && errno != ENOENT) exit(1); if (mount("/sys/fs/smackfs", "./syz-tmp/newroot/sys/fs/smackfs", NULL, bind_mount_flags, NULL) && errno != ENOENT) exit(1); if (mount("/proc/sys/fs/binfmt_misc", "./syz-tmp/newroot/proc/sys/fs/binfmt_misc", NULL, bind_mount_flags, NULL) && errno != ENOENT) exit(1); if (mkdir("./syz-tmp/newroot/syz-inputs", 0700)) exit(1); if (mount("/syz-inputs", "./syz-tmp/newroot/syz-inputs", NULL, bind_mount_flags | MS_RDONLY, NULL) && errno != ENOENT) exit(1); if (mkdir("./syz-tmp/pivot", 0777)) exit(1); if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) { if (chdir("./syz-tmp")) exit(1); } else { if (chdir("/")) exit(1); if (umount2("./pivot", MNT_DETACH)) exit(1); } if (chroot("./newroot")) exit(1); if (chdir("/")) exit(1); setup_gadgetfs(); setup_binderfs(); setup_fusectl(); } static void setup_gadgetfs() { if (mkdir("/dev/gadgetfs", 0777)) { } if (mount("gadgetfs", "/dev/gadgetfs", "gadgetfs", 0, NULL)) { } } static void setup_fusectl() { if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { } } static void setup_binderfs() { if (mkdir("/dev/binderfs", 0777)) { } if (mount("binder", "/dev/binderfs", "binder", 0, NULL)) { } if (symlink("/dev/binderfs", "./binderfs")) { } } static void loop(); static void sandbox_common() { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); if (getppid() == 1) exit(1); struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (200 << 20); setrlimit(RLIMIT_AS, &rlim); rlim.rlim_cur = rlim.rlim_max = 32 << 20; setrlimit(RLIMIT_MEMLOCK, &rlim); rlim.rlim_cur = rlim.rlim_max = 136 << 20; setrlimit(RLIMIT_FSIZE, &rlim); rlim.rlim_cur = rlim.rlim_max = 1 << 20; setrlimit(RLIMIT_STACK, &rlim); rlim.rlim_cur = rlim.rlim_max = 128 << 20; setrlimit(RLIMIT_CORE, &rlim); rlim.rlim_cur = rlim.rlim_max = 256; setrlimit(RLIMIT_NOFILE, &rlim); if (unshare(CLONE_NEWNS)) { } if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { } if (unshare(CLONE_NEWIPC)) { } if (unshare(0x02000000)) { } if (unshare(CLONE_NEWUTS)) { } if (unshare(CLONE_SYSVSEM)) { } typedef struct { const char* name; const char* value; } sysctl_t; static const sysctl_t sysctls[] = { {"/proc/sys/kernel/shmmax", "16777216"}, {"/proc/sys/kernel/shmall", "536870912"}, {"/proc/sys/kernel/shmmni", "1024"}, {"/proc/sys/kernel/msgmax", "8192"}, {"/proc/sys/kernel/msgmni", "1024"}, {"/proc/sys/kernel/msgmnb", "1024"}, {"/proc/sys/kernel/sem", "1024 1048576 500 1024"}, }; unsigned i; for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++) write_file(sysctls[i].name, sysctls[i].value); } static int wait_for_loop(int pid) { if (pid < 0) exit(1); int status = 0; while (waitpid(-1, &status, __WALL) != pid) { } return WEXITSTATUS(status); } static void drop_caps(void) { struct __user_cap_header_struct cap_hdr = {}; struct __user_cap_data_struct cap_data[2] = {}; cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; cap_hdr.pid = getpid(); if (syscall(SYS_capget, &cap_hdr, &cap_data)) exit(1); const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); cap_data[0].effective &= ~drop; cap_data[0].permitted &= ~drop; cap_data[0].inheritable &= ~drop; if (syscall(SYS_capset, &cap_hdr, &cap_data)) exit(1); } static int do_sandbox_none(void) { if (unshare(CLONE_NEWPID)) { } int pid = fork(); if (pid != 0) return wait_for_loop(pid); sandbox_common(); drop_caps(); if (unshare(CLONE_NEWNET)) { } write_file("/proc/sys/net/ipv4/ping_group_range", "0 65535"); sandbox_common_mount_tmpfs(); loop(); exit(1); } static void kill_and_wait(int pid, int* status) { kill(-pid, SIGKILL); kill(pid, SIGKILL); for (int i = 0; i < 100; i++) { if (waitpid(-1, status, WNOHANG | __WALL) == pid) return; usleep(1000); } DIR* dir = opendir("/sys/fs/fuse/connections"); if (dir) { for (;;) { struct dirent* ent = readdir(dir); if (!ent) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; char abort[300]; snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name); int fd = open(abort, O_WRONLY); if (fd == -1) { continue; } if (write(fd, abort, 1) < 0) { } close(fd); } closedir(dir); } else { } while (waitpid(-1, status, __WALL) != pid) { } } static void setup_test() { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); write_file("/proc/self/oom_score_adj", "1000"); } #define FALLOC_FL_KEEP_SIZE 0x01 #define FALLOC_FL_PUNCH_HOLE 0x02 #define FALLOC_FL_ZERO_RANGE 0x10 #define MREMAP_MAYMOVE 1 #define SYZ_FUSE_SR_SKIP_RETRIEVE_HANDLE 0x40000U #define SYZ_FUSE_SR_CLOSE_NOTIFY_AFTER_RETRIEVE 0x80000U #define SYZ_FUSE_SR_CLOSE_READ_AFTER_RETRIEVE 0x100000U #define SYZ_FUSE_SR_UMOUNT_AFTER_RETRIEVE 0x200000U #define SYZ_FUSE_SR_SLEEP_BEFORE_ABORT 0x400000U #define SYZ_FUSE_SR_SECOND_RETRIEVE_SAME_UNIQUE 0x800000U #define SYZ_FUSE_SR_SECOND_RETRIEVE_INTBIT_UNIQUE 0x1000000U #define SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH 0x2000000U #define SYZ_FUSE_SR_SPLICE_RETRIEVE_HANDLE 0x4000000U #define SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN 0x8000000U #define SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN 0x10000000U #define SYZ_FUSE_SR_SPLICE_SHORT_RETRIEVE 0x20000000U #define SYZ_FUSE_SR_REQUIRE_SPLICE_SUCCESS 0x40000000U #define SYZ_FUSE_SR_CHILD_POLL_LOOP 0x80000000U #define SYZ_FUSE_FR_SHORT_READ 0x1U #define SYZ_FUSE_FR_FAULT_MUNMAP 0x2U #define SYZ_FUSE_FR_FAULT_MPROTECT 0x4U #define SYZ_FUSE_FR_READV_SPLIT 0x8U #define SYZ_FUSE_FR_UMOUNT_DURING_READ 0x10U #define SYZ_FUSE_FR_UMOUNT_AFTER_READ 0x20U #define SYZ_FUSE_FR_CLOSE_NOTIFY_AFTER_QUEUE 0x40U #define SYZ_FUSE_FR_DRAIN_AFTER_ERROR 0x80U #define SYZ_FUSE_FR_FILE_ACTIVITY 0x100U #define SYZ_FUSE_FR_SECOND_RETRIEVE 0x200U #define SYZ_FUSE_DN_EXPIRE_FIRST 0x1U #define SYZ_FUSE_DN_DELETE_FIRST 0x2U #define SYZ_FUSE_DN_INVAL_INODE_FIRST 0x4U #define SYZ_FUSE_DN_INC_EPOCH_FIRST 0x8U #define SYZ_FUSE_DN_PRUNE_FIRST 0x10U #define SYZ_FUSE_DN_STORE 0x20U #define SYZ_FUSE_DN_RETRIEVE 0x40U #define SYZ_FUSE_DN_FILE_ACTIVITY 0x80U #define SYZ_FUSE_DN_FAULT_MPROTECT 0x100U #define SYZ_FUSE_DN_FAULT_MUNMAP 0x200U #define SYZ_FUSE_DN_READV_SPLIT 0x400U #define SYZ_FUSE_DN_UMOUNT_DURING_READ 0x800U #define SYZ_FUSE_DN_UMOUNT_AFTER_READ 0x1000U #define SYZ_FUSE_DN_DELETE_AFTER 0x2000U #define SYZ_FUSE_DN_PRUNE_AFTER 0x4000U #define SYZ_FUSE_DN_EXPIRE_AFTER 0x8000U #define SYZ_FUSE_DN_SECOND_RETRIEVE 0x10000U #define SYZ_FUSE_DN_DRAIN_AFTER 0x20000U #define SYZ_FUSE_DN_INVAL_INODE_AFTER 0x40000U #define SYZ_FUSE_DN_INC_EPOCH_AFTER 0x80000U #define SYZ_FUSE_DN_CLOSE_NOTIFY_AFTER_QUEUE 0x100000U #define SYZ_FUSE_CR_EXPIRE_STORM 0x1U #define SYZ_FUSE_CR_DELETE_STORM 0x2U #define SYZ_FUSE_CR_PRUNE_STORM 0x4U #define SYZ_FUSE_CR_INVAL_INODE_STORM 0x8U #define SYZ_FUSE_CR_INC_EPOCH_STORM 0x10U #define SYZ_FUSE_CR_CLOSE_NOTIFY_DURING_READ 0x20U #define SYZ_FUSE_CR_CLOSE_READ_DURING_READ 0x40U #define SYZ_FUSE_CR_UMOUNT_DURING_READ 0x80U #define SYZ_FUSE_CR_FAULT_MPROTECT 0x100U #define SYZ_FUSE_CR_FAULT_MUNMAP 0x200U #define SYZ_FUSE_CR_READV_SPLIT 0x400U #define SYZ_FUSE_CR_SECOND_RETRIEVE 0x800U #define SYZ_FUSE_CR_DRAIN_AFTER 0x1000U #define SYZ_FUSE_CR_FILE_ACTIVITY 0x2000U #define SYZ_FUSE_CR_STORE_FIRST 0x4000U #define SYZ_FUSE_CR_MULTI_PRUNE 0x8000U #define SYZ_FUSE_CR_DELETE_AFTER_READ 0x10000U #define SYZ_FUSE_CR_PRUNE_AFTER_READ 0x20000U #define SYZ_FUSE_CR_INVAL_AFTER_READ 0x40000U #define SYZ_FUSE_CR_INC_EPOCH_AFTER_READ 0x80000U #define SYZ_FUSE_CR_MATERIALIZE_STATEFUL 0x100000U #define SYZ_FUSE_CR_LIVE_HOLD_FAULTS 0x200000U #define SYZ_FUSE_CR_HIGH_BOUNDARY_STORE 0x400000U #define SYZ_FUSE_CR_STORE_RETRIEVE_AFTER_FAULT 0x800000U #define SYZ_FUSE_CR_RESEND_STORM 0x1000000U #define SYZ_FUSE_CR_POLL_STORM 0x2000000U #define SYZ_FUSE_CR_CLONE_NOTIFY_FD 0x4000000U #define SYZ_FUSE_CR_CLONE_READ_FD 0x8000000U #define SYZ_FUSE_CR_CLOSE_CLONE_AFTER_RETRIEVE 0x10000000U #define SYZ_FUSE_CR_CLOSE_CLONE_AFTER_LATE_NOTIFY 0x20000000U #define SYZ_FUSE_CR_ZERO_TIMEOUT_MATERIALIZE 0x40000000U #define SYZ_FUSE_CR_LATE_POSTCOPY_TEARDOWN 0x80000000U #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_MIDABORT 0x2000000000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_DRAIN 0x1000000000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_KEEP_NOTIFY 0x0800000000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_KEEP_MOUNT 0x0400000000000000ULL #define SYZ_FUSE_CR_MAGIC_MALFORMED_NOTIFY 0x0200000000000000ULL #define SYZ_FUSE_CR_MAGIC_EDGE_VALID_NOTIFY 0x0100000000000000ULL #define SYZ_FUSE_CR_MAGIC_EDGE_RACE_NOTIFY 0x0080000000000000ULL #define SYZ_FUSE_CR_MAGIC_EDGE_ABORT_NOTIFY 0x0040000000000000ULL #define SYZ_FUSE_CR_MAGIC_EDGE_PRESSURE_NOTIFY 0x0020000000000000ULL #define SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL 0x0010000000000000ULL #define SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_BEFORE_WAIT 0x0008000000000000ULL #define SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_DELAY 0x0004000000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_POSTCOPY_ABORT 0x0002000000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_POSTCOPY_PRESSURE 0x0001000000000000ULL #define SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT 0x0000800000000000ULL #define SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE 0x0000400000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_POSTCLOSE_ABORT 0x0000200000000000ULL #define SYZ_FUSE_CR_MAGIC_LATE_POSTCLOSE_PRESSURE 0x0000100000000000ULL #define SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_POLL 0x0000080000000000ULL #define SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT 0x0000040000000000ULL #define SYZ_FUSE_CR_MAGIC_EARLY_POSTABORT_FAULT 0x0000020000000000ULL #define SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_ASYNC 0x0000010000000000ULL #define SYZ_FUSE_CR_MAGIC_CLOSE_CLONE_BEFORE_LATE 0x0000008000000000ULL #define SYZ_FUSE_CR_MAGIC_FINAL_POSTPOLL_FAULT 0x0000004000000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_WINDOW 0x0000002000000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT 0x0000001000000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_MICRO 0x0000000800000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_POSTCLONE_STORE 0x0000000400000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY 0x0000000200000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_STORE_EARLY_ONLY 0x0000000100000000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PRECLONE_STORE 0x0000000040000000ULL #define SYZ_FUSE_CR_MAGIC_RESTORE_READ_AFTER_CLONE 0x0000000020000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_READ 0x0000000010000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_ONCE 0x0000000008000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_SANDWICH 0x0000000004000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_PRESTORE_FAULT 0x0000000002000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_PRESTORE_FAULT_AFTER_PRESSURE 0x0000000001000000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_RETRIEVE_DUP_UNIQUE 0x0000000000800000ULL #define SYZ_FUSE_CR_MAGIC_POSTCLONE_RETRIEVE_BASE_UNIQUE 0x0000000000400000ULL #define SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_RESEND 0x0000000000200000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND 0x0000000000100000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_ONESHOT 0x0000000000080000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_NOREPLY 0x0000000000040000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_DUP 0x0000000000020000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY 0x0000000000010000ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_UNREAD 0x0000000000000800ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY 0x0000000000000200ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY 0x0000000000000100ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_RELEASE 0x0000000000000080ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_PREDRAIN 0x0000000000000040ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_ONLY 0x0000000000000020ULL #define SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_NOTIFY_REPLY 0x0000000000000010ULL #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_ERROR_REPLY 0x0000000000000400ULL #define SYZ_FUSE_CR_MAGIC_LASTDEV_CLOSE_BEFORE_FINAL 0x8000000000000000ULL #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_TRUNC_REPLY 0x0000000000000004ULL #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_DUP_REPLY 0x0000000000000002ULL #define SYZ_FUSE_CR_MAGIC_RELEASE_RESEND_CONTINUE 0x0000000000000001ULL #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_SPLICE_READ 0x0000000000000008ULL #define SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ 0x0000000080000000ULL #define SYZ_FUSE_CR_MAGIC_CWD_PIN 0x2000000000000000ULL #define SYZ_FUSE_CR_MAGIC_EMPTY_EXTRA 0x4000000000000000ULL #define SYZ_FUSE_MD_EXPIRE_FILE 0x1U #define SYZ_FUSE_MD_DELETE_FILE2 0x2U #define SYZ_FUSE_MD_DELETE_DIR 0x4U #define SYZ_FUSE_MD_PRUNE_BEFORE 0x8U #define SYZ_FUSE_MD_PRUNE_AFTER 0x10U #define SYZ_FUSE_MD_INC_EPOCH_BEFORE 0x20U #define SYZ_FUSE_MD_INC_EPOCH_AFTER 0x40U #define SYZ_FUSE_MD_INVAL_CHILD 0x80U #define SYZ_FUSE_MD_STORE_FILE 0x100U #define SYZ_FUSE_MD_RETRIEVE_FILE 0x200U #define SYZ_FUSE_MD_RETRIEVE_CHILD 0x400U #define SYZ_FUSE_MD_FILE_ACTIVITY 0x800U #define SYZ_FUSE_MD_RENAME_STORM 0x1000U #define SYZ_FUSE_MD_UNLINK_STORM 0x2000U #define SYZ_FUSE_MD_FAULT_MPROTECT 0x4000U #define SYZ_FUSE_MD_FAULT_MUNMAP 0x8000U #define SYZ_FUSE_MD_READV_SPLIT 0x10000U #define SYZ_FUSE_MD_UMOUNT_AFTER 0x20000U #define SYZ_FUSE_MD_DRAIN_AFTER 0x40000U #define SYZ_FUSE_MD_DELETE_FILE_AFTER 0x80000U #define SYZ_FUSE_MD_CLOSE_NOTIFY_AFTER_QUEUE 0x100000U #define SYZ_FUSE_SF_FLIP_FILE_TO_DIR 0x1U #define SYZ_FUSE_SF_FLIP_DIR_TO_FILE 0x2U #define SYZ_FUSE_SF_ENOENT_ONCE 0x4U #define SYZ_FUSE_SF_ALIAS_OLD_INODE 0x8U #define SYZ_FUSE_SF_ZERO_TIMEOUT 0x10U #define SYZ_FUSE_SF_EXPIRE_FILE 0x20U #define SYZ_FUSE_SF_INC_EPOCH 0x40U #define SYZ_FUSE_SF_DELETE_DIR 0x80U #define SYZ_FUSE_SF_DELETE_ALIAS 0x100U #define SYZ_FUSE_SF_PRUNE_ALIAS 0x200U #define SYZ_FUSE_SF_INVAL_CHILD 0x400U #define SYZ_FUSE_SF_STORE_OLD 0x800U #define SYZ_FUSE_SF_RETRIEVE_OLD 0x1000U #define SYZ_FUSE_SF_FILE_ACTIVITY 0x2000U #define SYZ_FUSE_SF_RENAME_STORM 0x4000U #define SYZ_FUSE_SF_UNLINK_STORM 0x8000U #define SYZ_FUSE_SF_FAULT_MPROTECT 0x10000U #define SYZ_FUSE_SF_FAULT_MUNMAP 0x20000U #define SYZ_FUSE_SF_READV_SPLIT 0x40000U #define SYZ_FUSE_SF_UMOUNT_AFTER 0x80000U #define SYZ_FUSE_SF_CLOSE_NOTIFY_AFTER_RETRIEVE 0x100000U #define SYZ_FUSE_SF_PRUNE_AFTER_FLIP 0x200000U #define SYZ_FUSE_SF_DELETE_FILE_AFTER_FLIP 0x400000U #define SYZ_FUSE_SF_POLL_FILE 0x800000U #define SYZ_FUSE_SF_CWD_PIN 0x1000000U #define SYZ_FUSE_NSP_STORE_EXTEND 0x1U #define SYZ_FUSE_NSP_SECOND_STORE 0x2U #define SYZ_FUSE_NSP_EXPIRE_FILE 0x4U #define SYZ_FUSE_NSP_DELETE_FILE 0x8U #define SYZ_FUSE_NSP_DELETE_ALIAS 0x10U #define SYZ_FUSE_NSP_PRUNE_ALIAS 0x20U #define SYZ_FUSE_NSP_DELETE_DIR 0x40U #define SYZ_FUSE_NSP_INC_EPOCH 0x80U #define SYZ_FUSE_NSP_RETRIEVE_FILE 0x100U #define SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE 0x200U #define SYZ_FUSE_NSP_FILE_ACTIVITY 0x400U #define SYZ_FUSE_NSP_FAULT_MPROTECT 0x800U #define SYZ_FUSE_NSP_FAULT_MUNMAP 0x1000U #define SYZ_FUSE_NSP_READV_SPLIT 0x2000U #define SYZ_FUSE_NSP_UMOUNT_AFTER 0x4000U #define SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE 0x8000U #define SYZ_FUSE_NSP_REVALIDATE_AFTER 0x10000U #define SYZ_FUSE_NSP_PRE_RELOOKUP 0x20000U #define SYZ_FUSE_NSP_STRONG_STORE_OFFSET 0x40000U #define SYZ_FUSE_NSP_VALID_PRUNE_BATCH 0x80000U #define SYZ_FUSE_NSP_POST_CLOSE_PRUNE 0x100000U #define SYZ_FUSE_NSP_STRESS_LOOPS 0x200000U #define SYZ_FUSE_NSP_STORE_BEFORE_LIFETIME 0x400000U #define SYZ_FUSE_NSP_LIVE_HOLD_FAULTS 0x800000U #define SYZ_FUSE_NSP_DELAY_RETRIEVE_DRAIN 0x1000000U #define SYZ_FUSE_NSP_CLONE_DRAIN_FD 0x2000000U #define SYZ_FUSE_NSP_CLOSE_CLONE_BEFORE_PRUNE 0x4000000U #define SYZ_FUSE_NSP_FAULTING_REPLY_RACE 0x8000000U #define SYZ_FUSE_NSP_READ_REPLY_RACE 0x10000000U #define SYZ_FUSE_INT_REQ_BIT (1ULL << 63) #define SYZ_FUSE_FOPEN_DIRECT_IO 0x1U #define SPLICE_F_MOVE 1 #define SPLICE_F_NONBLOCK 2 #define SPLICE_F_MORE 4 #define SPLICE_F_GIFT 8 #define F_SETPIPE_SZ 1031 #define F_GETPIPE_SZ 1032 #define FUSE_MIN_READ_BUFFER 8192 enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, FUSE_GETATTR = 3, FUSE_SETATTR = 4, FUSE_READLINK = 5, FUSE_SYMLINK = 6, FUSE_MKNOD = 8, FUSE_MKDIR = 9, FUSE_UNLINK = 10, FUSE_RMDIR = 11, FUSE_RENAME = 12, FUSE_LINK = 13, FUSE_OPEN = 14, FUSE_READ = 15, FUSE_WRITE = 16, FUSE_STATFS = 17, FUSE_RELEASE = 18, FUSE_FSYNC = 20, FUSE_SETXATTR = 21, FUSE_GETXATTR = 22, FUSE_LISTXATTR = 23, FUSE_REMOVEXATTR = 24, FUSE_FLUSH = 25, FUSE_INIT = 26, FUSE_OPENDIR = 27, FUSE_READDIR = 28, FUSE_RELEASEDIR = 29, FUSE_FSYNCDIR = 30, FUSE_GETLK = 31, FUSE_SETLK = 32, FUSE_SETLKW = 33, FUSE_ACCESS = 34, FUSE_CREATE = 35, FUSE_INTERRUPT = 36, FUSE_BMAP = 37, FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, FUSE_BATCH_FORGET = 42, FUSE_FALLOCATE = 43, FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, FUSE_COPY_FILE_RANGE = 47, FUSE_SETUPMAPPING = 48, FUSE_REMOVEMAPPING = 49, FUSE_SYNCFS = 50, FUSE_TMPFILE = 51, FUSE_STATX = 52, CUSE_INIT = 4096, CUSE_INIT_BSWAP_RESERVED = 1048576, FUSE_INIT_BSWAP_RESERVED = 436207616, }; struct fuse_in_header { uint32_t len; uint32_t opcode; uint64_t unique; uint64_t nodeid; uint32_t uid; uint32_t gid; uint32_t pid; uint32_t padding; }; struct fuse_out_header { uint32_t len; uint32_t error; uint64_t unique; }; struct fuse_poll_in { uint64_t fh; uint64_t kh; uint32_t flags; uint32_t events; }; struct fuse_notify_poll_wakeup_out { uint64_t kh; }; struct fuse_notify_poll_msg { uint32_t len; uint32_t code; uint64_t unique; struct fuse_notify_poll_wakeup_out out; } __attribute__((packed)); struct fuse_notify_empty_msg { uint32_t len; uint32_t code; uint64_t unique; } __attribute__((packed)); struct fuse_notify_retrieve_msg { uint32_t len; uint32_t code; uint64_t unique; uint64_t notify_unique; uint64_t nodeid; uint64_t offset; uint32_t size; uint32_t padding; } __attribute__((packed)); struct fuse_notify_store_head { uint32_t len; uint32_t code; uint64_t unique; uint64_t nodeid; uint64_t offset; uint32_t size; uint32_t padding; } __attribute__((packed)); struct fuse_notify_inval_entry_head { uint32_t len; uint32_t code; uint64_t unique; uint64_t parent; uint32_t namelen; uint32_t flags; } __attribute__((packed)); struct fuse_notify_delete_head { uint32_t len; uint32_t code; uint64_t unique; uint64_t parent; uint64_t child; uint32_t namelen; uint32_t padding; } __attribute__((packed)); struct fuse_notify_inval_inode_msg { uint32_t len; uint32_t code; uint64_t unique; uint64_t ino; long long off; long long len2; } __attribute__((packed)); struct fuse_notify_prune_one_msg { uint32_t len; uint32_t code; uint64_t unique; uint32_t count; uint32_t padding; uint64_t spare; uint64_t nodeid; } __attribute__((packed)); #define FUSE_NOTIFY_POLL_CODE 1 #define FUSE_NOTIFY_INVAL_INODE_CODE 2 #define FUSE_NOTIFY_INVAL_ENTRY_CODE 3 #define FUSE_NOTIFY_STORE_CODE 4 #define FUSE_NOTIFY_RETRIEVE_CODE 5 #define FUSE_NOTIFY_DELETE_CODE 6 #define FUSE_NOTIFY_RESEND_CODE 7 #define FUSE_NOTIFY_INC_EPOCH_CODE 8 #define FUSE_NOTIFY_PRUNE_CODE 9 #define FUSE_EXPIRE_ONLY_FLAG 1 #define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) struct syz_fuse_req_out { struct fuse_out_header* init; struct fuse_out_header* lseek; struct fuse_out_header* bmap; struct fuse_out_header* poll; struct fuse_out_header* getxattr; struct fuse_out_header* lk; struct fuse_out_header* statfs; struct fuse_out_header* write; struct fuse_out_header* read; struct fuse_out_header* open; struct fuse_out_header* attr; struct fuse_out_header* entry; struct fuse_out_header* dirent; struct fuse_out_header* direntplus; struct fuse_out_header* create_open; struct fuse_out_header* ioctl; struct fuse_out_header* statx; }; struct syz_fuse_retrieve_req { uint64_t notify_unique; uint64_t nodeid; uint64_t offset; uint32_t size; uint32_t mode; }; struct syz_fuse_store_retrieve_req { uint64_t notify_unique; uint64_t nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t mode; uint32_t wait_ms; }; struct syz_fuse_fault_retrieve_req { uint64_t notify_unique; uint64_t nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; }; struct syz_fuse_expire_req { uint64_t parent; uint64_t old_nodeid; uint64_t new_nodeid; uint32_t mode; uint32_t wait_ms; }; struct syz_fuse_dentry_notify_req { uint64_t notify_unique; uint64_t nodeid; uint64_t parent_nodeid; uint64_t child_nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; uint32_t loops; }; struct syz_fuse_cleanup_race_req { uint64_t notify_unique; uint64_t nodeid; uint64_t parent_nodeid; uint64_t child_nodeid; uint64_t alt_nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; uint32_t loops; }; struct syz_fuse_multidentry_req { uint64_t notify_unique; uint64_t file_nodeid; uint64_t file2_nodeid; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; uint32_t loops; }; struct syz_fuse_stateflip_req { uint64_t notify_unique; uint64_t old_file_nodeid; uint64_t new_file_nodeid; uint64_t alias_nodeid; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t child2_nodeid; uint64_t subdir_nodeid; uint64_t subchild_nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; uint32_t loops; uint32_t flip_after; }; struct syz_fuse_notify_sink_req { uint64_t notify_unique; uint64_t file_nodeid; uint64_t new_file_nodeid; uint64_t alias_nodeid; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t child2_nodeid; uint64_t subdir_nodeid; uint64_t subchild_nodeid; uint64_t store_offset; uint64_t retrieve_offset; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t wait_ms; uint32_t loops; }; static void syz_fuse_patch_entry_state(struct fuse_out_header* out_hdr, uint64_t nodeid, uint64_t generation, uint32_t mode, uint64_t size, int zero_timeout); struct syz_fuse_fallback_req_out { struct syz_fuse_req_out req; struct { struct fuse_out_header hdr; char payload[64]; } init; struct { struct fuse_out_header hdr; char payload[128]; } entry; struct { struct fuse_out_header hdr; char payload[104]; } attr; struct { struct fuse_out_header hdr; char payload[16]; } open; struct { struct fuse_out_header hdr; char payload[8]; } poll; struct { struct fuse_out_header hdr; char payload[4096]; } read; struct { struct fuse_out_header hdr; char payload[256]; } dirent; struct { struct fuse_out_header hdr; char payload[256]; } direntplus; }; static void syz_fuse_setup_hdr(struct fuse_out_header* hdr, uint32_t len) { hdr->len = len; hdr->error = 0; hdr->unique = 0; } static void syz_fuse_prepare_fallback_req_out( struct syz_fuse_fallback_req_out* out) { char* payload; memset(out, 0, sizeof(*out)); syz_fuse_setup_hdr(&out->init.hdr, sizeof(out->init.hdr) + sizeof(out->init.payload)); payload = out->init.payload; *(uint32_t*)(payload + 0) = 7; *(uint32_t*)(payload + 4) = 38; *(uint32_t*)(payload + 8) = 0; *(uint32_t*)(payload + 12) = (1U << 5) | (1U << 22); *(uint16_t*)(payload + 16) = 32; *(uint16_t*)(payload + 18) = 16; *(uint32_t*)(payload + 20) = 16 << 20; *(uint32_t*)(payload + 24) = 1; *(uint16_t*)(payload + 28) = 4096; *(uint16_t*)(payload + 30) = 0; *(uint32_t*)(payload + 32) = 0; syz_fuse_setup_hdr(&out->entry.hdr, sizeof(out->entry.hdr) + sizeof(out->entry.payload)); syz_fuse_patch_entry_state(&out->entry.hdr, 2, 1, S_IFREG | 0600, 0x1000000, 0); syz_fuse_setup_hdr(&out->attr.hdr, sizeof(out->attr.hdr) + sizeof(out->attr.payload)); payload = out->attr.payload; *(uint64_t*)(payload + 0) = 60; *(uint32_t*)(payload + 8) = 0; *(uint64_t*)(payload + 16) = 2; *(uint64_t*)(payload + 24) = 0x1000000; *(uint32_t*)(payload + 76) = S_IFREG | 0600; *(uint32_t*)(payload + 80) = 1; syz_fuse_setup_hdr(&out->open.hdr, sizeof(out->open.hdr) + sizeof(out->open.payload)); payload = out->open.payload; *(uint64_t*)(payload + 0) = 1; *(uint32_t*)(payload + 8) = 0; syz_fuse_setup_hdr(&out->poll.hdr, sizeof(out->poll.hdr) + sizeof(out->poll.payload)); payload = out->poll.payload; *(uint32_t*)(payload + 0) = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; *(uint32_t*)(payload + 4) = 0; syz_fuse_setup_hdr(&out->read.hdr, sizeof(out->read.hdr)); syz_fuse_setup_hdr(&out->dirent.hdr, sizeof(out->dirent.hdr)); syz_fuse_setup_hdr(&out->direntplus.hdr, sizeof(out->direntplus.hdr)); out->req.init = &out->init.hdr; out->req.lseek = &out->init.hdr; out->req.bmap = &out->init.hdr; out->req.poll = &out->poll.hdr; out->req.getxattr = &out->init.hdr; out->req.lk = &out->init.hdr; out->req.statfs = &out->init.hdr; out->req.write = &out->init.hdr; out->req.read = &out->read.hdr; out->req.open = &out->open.hdr; out->req.attr = &out->attr.hdr; out->req.entry = &out->entry.hdr; out->req.dirent = &out->dirent.hdr; out->req.direntplus = &out->direntplus.hdr; out->req.create_open = &out->entry.hdr; out->req.ioctl = &out->init.hdr; out->req.statx = &out->attr.hdr; } static void syz_fuse_kmsg2(const char* tag, long a, long b); static int fuse_send_response(int fd, const struct fuse_in_header* in_hdr, struct fuse_out_header* out_hdr) { if (!out_hdr) { return -1; } out_hdr->unique = in_hdr->unique; if (in_hdr->opcode == FUSE_INIT && out_hdr->len >= sizeof(*out_hdr) + 32) { char* payload = (char*)(out_hdr + 1); syz_fuse_kmsg2("cleanup_init_reply_max_write_pages", *(uint32_t*)(payload + 20), *(uint16_t*)(payload + 28)); syz_fuse_kmsg2("cleanup_init_reply_flags_minor", *(uint32_t*)(payload + 12), *(uint32_t*)(payload + 4)); } if ((in_hdr->opcode == FUSE_OPEN || in_hdr->opcode == FUSE_OPENDIR) && out_hdr->len >= sizeof(*out_hdr) + 12) { char* payload = (char*)(out_hdr + 1); syz_fuse_kmsg2("nsp_open_reply_flags", *(uint32_t*)(payload + 8), in_hdr->unique); } if (write(fd, out_hdr, out_hdr->len) == -1) { return -1; } return 0; } static int syz_fuse_pick_response(struct syz_fuse_req_out* req_out, uint32_t opcode, struct fuse_out_header** out_hdr, const char* caller) { *out_hdr = NULL; switch (opcode) { case FUSE_GETATTR: case FUSE_SETATTR: *out_hdr = req_out->attr; break; case FUSE_LOOKUP: case FUSE_SYMLINK: case FUSE_LINK: case FUSE_MKNOD: case FUSE_MKDIR: *out_hdr = req_out->entry; break; case FUSE_OPEN: case FUSE_OPENDIR: *out_hdr = req_out->open; break; case FUSE_STATFS: *out_hdr = req_out->statfs; break; case FUSE_RMDIR: case FUSE_RENAME: case FUSE_RENAME2: case FUSE_FALLOCATE: case FUSE_SETXATTR: case FUSE_REMOVEXATTR: case FUSE_FSYNCDIR: case FUSE_FSYNC: case FUSE_SETLKW: case FUSE_SETLK: case FUSE_ACCESS: case FUSE_FLUSH: case FUSE_RELEASE: case FUSE_RELEASEDIR: case FUSE_UNLINK: case FUSE_DESTROY: *out_hdr = req_out->init; if (!*out_hdr) { return -1; } (*out_hdr)->len = sizeof(struct fuse_out_header); break; case FUSE_READ: *out_hdr = req_out->read; break; case FUSE_READDIR: *out_hdr = req_out->dirent; break; case FUSE_READDIRPLUS: *out_hdr = req_out->direntplus; break; case FUSE_INIT: *out_hdr = req_out->init; break; case FUSE_LSEEK: *out_hdr = req_out->lseek; break; case FUSE_GETLK: *out_hdr = req_out->lk; break; case FUSE_BMAP: *out_hdr = req_out->bmap; break; case FUSE_POLL: *out_hdr = req_out->poll; break; case FUSE_GETXATTR: case FUSE_LISTXATTR: *out_hdr = req_out->getxattr; break; case FUSE_WRITE: case FUSE_COPY_FILE_RANGE: *out_hdr = req_out->write; break; case FUSE_FORGET: case FUSE_BATCH_FORGET: case FUSE_NOTIFY_REPLY: return 1; case FUSE_CREATE: *out_hdr = req_out->create_open; break; case FUSE_IOCTL: *out_hdr = req_out->ioctl; break; case FUSE_STATX: *out_hdr = req_out->statx; break; default: return -1; } if (!*out_hdr) { return -1; } return 0; } static volatile long syz_fuse_handle_req(volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a3; struct fuse_out_header* out_hdr = NULL; char* buf = (char*)a1; int buf_len = (int)a2; int fd = (int)a0; if (!req_out) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } int ret = read(fd, buf, buf_len); if (ret == -1) { return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { return -1; } const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { return -1; } int pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, "syz_fuse_handle_req"); if (pick < 0) return -1; if (pick > 0) return 0; return fuse_send_response(fd, in_hdr, out_hdr); } static void syz_fuse_sleep_ms(unsigned int ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (long)(ms % 1000) * 1000000L; nanosleep(&ts, NULL); } static void syz_fuse_wait_or_kill(pid_t pid) { int status = 0; for (int i = 0; i < 20; i++) { if (waitpid(pid, &status, WNOHANG) == pid) return; syz_fuse_sleep_ms(5); } kill(pid, SIGKILL); waitpid(pid, &status, 0); } static int syz_fuse_wait_readable(int fd, int timeout_ms) { struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN | POLLERR | POLLHUP; pfd.revents = 0; int ret = poll(&pfd, 1, timeout_ms); if (ret <= 0) { if (ret == 0) errno = ETIMEDOUT; return -1; } if (!(pfd.revents & (POLLIN | POLLERR | POLLHUP))) { errno = EAGAIN; return -1; } return 0; } static int syz_fuse_send_inval_entry(int fd, uint64_t parent, const char* name, uint32_t flags) { char notify[sizeof(struct fuse_notify_inval_entry_head) + 256]; struct fuse_notify_inval_entry_head* head = (struct fuse_notify_inval_entry_head*)notify; size_t namelen; if (!name) return -1; namelen = strnlen(name, 255); if (namelen == 0 || namelen >= 255) return -1; memset(notify, 0, sizeof(notify)); head->len = sizeof(*head) + namelen + 1; head->code = FUSE_NOTIFY_INVAL_ENTRY_CODE; head->parent = parent; head->namelen = namelen; head->flags = flags; memcpy(notify + sizeof(*head), name, namelen); if (write(fd, notify, head->len) == -1) { return -1; } return 0; } static int syz_fuse_send_delete(int fd, uint64_t parent, uint64_t child, const char* name) { char notify[sizeof(struct fuse_notify_delete_head) + 256]; struct fuse_notify_delete_head* head = (struct fuse_notify_delete_head*)notify; size_t namelen; if (!name) return -1; namelen = strnlen(name, 255); if (namelen == 0 || namelen >= 255) return -1; memset(notify, 0, sizeof(notify)); head->len = sizeof(*head) + namelen + 1; head->code = FUSE_NOTIFY_DELETE_CODE; head->parent = parent; head->child = child; head->namelen = namelen; memcpy(notify + sizeof(*head), name, namelen); if (write(fd, notify, head->len) == -1) { return -1; } return 0; } static int syz_fuse_send_inval_inode(int fd, uint64_t nodeid, uint64_t off, uint64_t len) { struct fuse_notify_inval_inode_msg notify; if (!nodeid) nodeid = 2; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_INVAL_INODE_CODE; notify.ino = nodeid; notify.off = (long long)off; notify.len2 = (long long)len; if (write(fd, ¬ify, sizeof(notify)) == -1) { return -1; } return 0; } static int syz_fuse_send_prune_one(int fd, uint64_t nodeid) { struct fuse_notify_prune_one_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_PRUNE_CODE; notify.count = 1; notify.nodeid = nodeid; if (write(fd, ¬ify, sizeof(notify)) == -1) { return -1; } return 0; } static int syz_fuse_send_prune_vec(int fd, uint64_t nodeid, uint64_t alt_nodeid, uint64_t parent, uint64_t child) { struct { uint32_t len; uint32_t code; uint64_t unique; uint32_t count; uint32_t padding; uint64_t spare; uint64_t nodeids[8]; } __attribute__((packed)) notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_PRUNE_CODE; notify.count = 8; notify.nodeids[0] = nodeid; notify.nodeids[1] = alt_nodeid ? alt_nodeid : nodeid + 1; notify.nodeids[2] = child; notify.nodeids[3] = parent; notify.nodeids[4] = 0; notify.nodeids[5] = 1; notify.nodeids[6] = nodeid ^ 0x80000000ULL; notify.nodeids[7] = ~0ULL; if (write(fd, ¬ify, sizeof(notify)) == -1) { return -1; } return 0; } static int syz_fuse_send_prune_valid_vec(int fd, const uint64_t* nodeids, uint32_t nodeid_count, uint32_t count) { struct fuse_notify_prune_valid_head { uint32_t len; uint32_t code; uint64_t unique; uint32_t count; uint32_t padding; uint64_t spare; } __attribute__((packed)); struct fuse_notify_prune_valid_head* head; char* notify; size_t len; uint64_t* ids; ssize_t written; if (!nodeids || !nodeid_count) return -1; if (!count) count = nodeid_count; if (count > 1024) count = 1024; len = sizeof(*head) + sizeof(uint64_t) * count; notify = (char*)malloc(len); if (!notify) return -1; memset(notify, 0, len); head = (struct fuse_notify_prune_valid_head*)notify; head->len = len; head->code = FUSE_NOTIFY_PRUNE_CODE; head->count = count; ids = (uint64_t*)(notify + sizeof(*head)); for (uint32_t i = 0; i < count; i++) ids[i] = nodeids[i % nodeid_count]; written = write(fd, notify, len); free(notify); if (written == -1) { return -1; } if ((size_t)written != len) { return -1; } return 0; } static void syz_fuse_kmsg2(const char* tag, long a, long b); static int syz_fuse_send_store_msg(int fd, uint64_t nodeid, uint64_t offset, uint32_t size); static int syz_fuse_send_retrieve_msg(int fd, uint64_t notify_unique, uint64_t nodeid, uint64_t offset, uint32_t size); static int syz_fuse_send_inc_epoch(int fd) { struct fuse_notify_empty_msg notify; ssize_t written; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_INC_EPOCH_CODE; written = write(fd, ¬ify, sizeof(notify)); syz_fuse_kmsg2("send_inc_epoch", written, written < 0 ? errno : 0); if (written == -1) { return -1; } return 0; } static int syz_fuse_send_empty_extra(int fd, int code, uint32_t extra_len) { struct fuse_notify_empty_msg* notify; size_t len = sizeof(*notify) + extra_len; ssize_t written; if (extra_len > 1048576) extra_len = 1048576; len = sizeof(*notify) + extra_len; notify = (struct fuse_notify_empty_msg*)malloc(len); if (!notify) return -1; memset(notify, 0x5a, len); notify->len = len; notify->code = code; notify->unique = 0; written = write(fd, notify, len); syz_fuse_kmsg2("send_empty_extra", code, written < 0 ? -errno : written); free(notify); if (written == -1) return -1; return 0; } static void syz_fuse_send_malformed_notify_set(int fd, uint64_t nodeid, uint64_t parent, uint64_t child, const char* name, uint64_t offset, uint32_t size) { char buf[8192]; ssize_t written; size_t namelen; if (fd < 0) return; if (!nodeid) nodeid = 2; if (!parent) parent = 1; if (!child) child = nodeid; if (!name) name = "file"; namelen = strlen(name); if (namelen > 128) namelen = 128; if (!size) size = 4096; memset(buf, 0x5b, sizeof(buf)); { struct fuse_notify_store_head* head = (struct fuse_notify_store_head*)buf; head->len = sizeof(*head) + 0x1000; head->code = FUSE_NOTIFY_STORE_CODE; head->nodeid = nodeid; head->offset = offset; head->size = 0x1000; written = write(fd, buf, sizeof(*head) + 64); syz_fuse_kmsg2("send_malformed_store_short", written, written < 0 ? -errno : head->len); } memset(buf, 0x6c, sizeof(buf)); { struct fuse_notify_store_head* head = (struct fuse_notify_store_head*)buf; head->len = sizeof(*head) + 1; head->code = FUSE_NOTIFY_STORE_CODE; head->nodeid = nodeid; head->offset = offset + 4095; head->size = 4096; written = write(fd, buf, sizeof(*head) + 4096); syz_fuse_kmsg2("send_malformed_store_long", written, written < 0 ? -errno : head->len); } memset(buf, 0x7d, sizeof(buf)); { struct fuse_notify_retrieve_msg* msg = (struct fuse_notify_retrieve_msg*)buf; msg->len = sizeof(*msg) + 0x1000; msg->code = FUSE_NOTIFY_RETRIEVE_CODE; msg->notify_unique = 0xfacefeed; msg->nodeid = nodeid; msg->offset = offset; msg->size = 0x1000; written = write(fd, buf, sizeof(*msg)); syz_fuse_kmsg2("send_malformed_retrieve_short", written, written < 0 ? -errno : msg->len); } memset(buf, 0x33, sizeof(buf)); { struct fuse_notify_delete_head* head = (struct fuse_notify_delete_head*)buf; head->len = sizeof(*head) + namelen; head->code = FUSE_NOTIFY_DELETE_CODE; head->parent = parent; head->child = child; head->namelen = 4096; memcpy(buf + sizeof(*head), name, namelen); written = write(fd, buf, sizeof(*head) + namelen); syz_fuse_kmsg2("send_malformed_delete_name", written, written < 0 ? -errno : head->namelen); } memset(buf, 0x44, sizeof(buf)); { struct fuse_notify_inval_entry_head* head = (struct fuse_notify_inval_entry_head*)buf; head->len = sizeof(*head) + namelen; head->code = FUSE_NOTIFY_INVAL_ENTRY_CODE; head->parent = parent; head->namelen = 4096; head->flags = FUSE_EXPIRE_ONLY_FLAG; memcpy(buf + sizeof(*head), name, namelen); written = write(fd, buf, sizeof(*head) + namelen); syz_fuse_kmsg2("send_malformed_inval_name", written, written < 0 ? -errno : head->namelen); } memset(buf, 0x55, sizeof(buf)); { struct fuse_notify_prune_one_msg* msg = (struct fuse_notify_prune_one_msg*)buf; msg->len = sizeof(*msg); msg->code = FUSE_NOTIFY_PRUNE_CODE; msg->count = 513; msg->nodeid = nodeid; written = write(fd, buf, sizeof(*msg)); syz_fuse_kmsg2("send_malformed_prune_count", written, written < 0 ? -errno : msg->count); } } static void syz_fuse_send_edge_valid_notify_set(int fd, uint64_t nodeid, uint64_t alt_nodeid, uint64_t parent, uint64_t child, const char* name, uint64_t offset, uint32_t size, uint64_t notify_unique) { uint64_t prune_nodeids[4]; uint64_t store_offsets[6]; uint32_t store_sizes[6]; uint64_t inval_lens[7]; uint32_t ok = 0; uint32_t fail = 0; if (fd < 0) return; if (!nodeid) nodeid = 2; if (!alt_nodeid) alt_nodeid = nodeid; if (!parent) parent = 1; if (!child) child = nodeid; if (!name) name = "file"; if (!size) size = 4096; if (!notify_unique) notify_unique = 0xace00000; store_offsets[0] = offset; store_offsets[1] = offset ? offset - 1 : 0; store_offsets[2] = offset + 1; store_offsets[3] = 0x7ffffffffffff000ULL; store_offsets[4] = 0x7ffffffffffffffeULL; store_offsets[5] = 0x7fffffff00001000ULL; store_sizes[0] = size; store_sizes[1] = 1; store_sizes[2] = 4097; store_sizes[3] = 1; store_sizes[4] = 2; store_sizes[5] = 4096; syz_fuse_kmsg2("edge_valid_start", offset, size); for (uint32_t i = 0; i < 6; i++) { if (syz_fuse_send_store_msg(fd, nodeid, store_offsets[i], store_sizes[i]) == 0) ok++; else fail++; if (syz_fuse_send_retrieve_msg(fd, notify_unique + 0x100 + i, nodeid, store_offsets[i], store_sizes[i]) == 0) ok++; else fail++; } syz_fuse_kmsg2("edge_valid_store_retrieve", ok, fail); ok = 0; fail = 0; inval_lens[0] = 0; inval_lens[1] = 1; inval_lens[2] = 4095; inval_lens[3] = 4096; inval_lens[4] = 4097; inval_lens[5] = 0x7fffffffffffffffULL; inval_lens[6] = 0xffffffffffffffffULL; for (uint32_t i = 0; i < 7; i++) { if (syz_fuse_send_inval_inode(fd, nodeid, i == 6 ? 0 : offset, inval_lens[i]) == 0) ok++; else fail++; } syz_fuse_kmsg2("edge_valid_inval_inode", ok, fail); ok = 0; fail = 0; if (syz_fuse_send_inval_entry(fd, parent, name, FUSE_EXPIRE_ONLY_FLAG) == 0) ok++; else fail++; if (syz_fuse_send_delete(fd, parent, child, name) == 0) ok++; else fail++; if (syz_fuse_send_inval_entry(fd, parent, name, 0) == 0) ok++; else fail++; if (syz_fuse_send_delete(fd, parent, child, name) == 0) ok++; else fail++; syz_fuse_kmsg2("edge_valid_dentry", ok, fail); ok = 0; fail = 0; prune_nodeids[0] = nodeid; prune_nodeids[1] = alt_nodeid; prune_nodeids[2] = child; prune_nodeids[3] = parent; if (syz_fuse_send_prune_valid_vec(fd, prune_nodeids, 4, 1) == 0) ok++; else fail++; if (syz_fuse_send_prune_valid_vec(fd, prune_nodeids, 4, 2) == 0) ok++; else fail++; if (syz_fuse_send_prune_valid_vec(fd, prune_nodeids, 4, 16) == 0) ok++; else fail++; if (syz_fuse_send_prune_valid_vec(fd, prune_nodeids, 4, 128) == 0) ok++; else fail++; if (syz_fuse_send_prune_valid_vec(fd, prune_nodeids, 4, 513) == 0) ok++; else fail++; (void)syz_fuse_send_inc_epoch(fd); syz_fuse_kmsg2("edge_valid_prune_epoch", ok, fail); } static void syz_fuse_abort_all_connections_now(const char* tag) { DIR* dir; uint32_t ok = 0; uint32_t fail = 0; setup_fusectl(); dir = opendir("/sys/fs/fuse/connections"); if (!dir) { syz_fuse_kmsg2(tag, 0, errno); return; } for (;;) { struct dirent* ent = readdir(dir); char path[300]; int fd; if (!ent) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; snprintf(path, sizeof(path), "/sys/fs/fuse/connections/%s/abort", ent->d_name); fd = open(path, O_WRONLY); if (fd == -1) { fail++; continue; } if (write(fd, "1", 1) == 1) ok++; else fail++; close(fd); } closedir(dir); syz_fuse_kmsg2(tag, ok, fail); } static uint32_t syz_fuse_open_abort_fds(int* fds, uint32_t maxfds, const char* tag) { DIR* dir; uint32_t n = 0; uint32_t fail = 0; if (!fds || !maxfds) return 0; setup_fusectl(); dir = opendir("/sys/fs/fuse/connections"); if (!dir) { syz_fuse_kmsg2(tag, 0, errno); return 0; } for (;;) { struct dirent* ent = readdir(dir); char path[300]; int fd; if (!ent || n >= maxfds) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; snprintf(path, sizeof(path), "/sys/fs/fuse/connections/%s/abort", ent->d_name); fd = open(path, O_WRONLY); if (fd == -1) { fail++; continue; } fds[n++] = fd; } closedir(dir); syz_fuse_kmsg2(tag, n, fail); return n; } static void syz_fuse_close_abort_fds(int* fds, uint32_t nfds) { if (!fds) return; for (uint32_t i = 0; i < nfds; i++) { if (fds[i] >= 0) { close(fds[i]); fds[i] = -1; } } } static void syz_fuse_edge_memory_pressure(const char* tag, uint32_t loops) { size_t len = 64 << 20; volatile char* p; uint32_t touched = 0; if (loops > 48) len = 512 << 20; else if (loops > 16) len = 256 << 20; syz_fuse_kmsg2("cleanup_edge_memory_pressure_loops", loops, len); for (;;) { p = (volatile char*)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (p != MAP_FAILED) break; if (len <= (32 << 20)) { syz_fuse_kmsg2(tag, -1, errno); return; } len >>= 1; } for (size_t off = 0; off < len; off += 4096) { p[off] = (char)(off >> 12); touched++; } (void)madvise((void*)p, len, MADV_DONTNEED); munmap((void*)p, len); syz_fuse_kmsg2(tag, len, touched); } static int syz_fuse_send_resend(int fd) { struct fuse_notify_empty_msg notify; int ret; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_RESEND_CODE; syz_fuse_kmsg2("cleanup_send_resend", fd, 0); ret = write(fd, ¬ify, sizeof(notify)); if (ret == -1) { syz_fuse_kmsg2("cleanup_send_resend_fail", fd, errno); return -1; } syz_fuse_kmsg2("cleanup_send_resend_ok", fd, ret); return 0; } static void syz_fuse_send_resend_burst(int fd, uint32_t loops, const char* tag, uint64_t aux) { if (fd < 0) return; if (loops < 1) loops = 1; if (loops > 32) loops = 32; syz_fuse_kmsg2(tag, loops, aux); for (uint32_t i = 0; i < loops; i++) { (void)syz_fuse_send_resend(fd); if ((i & 3) == 3) syz_fuse_sleep_ms(1); } } static int syz_fuse_send_poll_kh(int fd, uint64_t kh) { struct fuse_notify_poll_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_POLL_CODE; notify.out.kh = kh; if (write(fd, ¬ify, sizeof(notify)) == -1) { return -1; } return 0; } static void syz_fuse_send_poll_kh_burst(int fd, uint64_t kh, uint32_t loops, const char* tag, const char* done_tag) { uint32_t ok = 0; uint32_t fail = 0; if (fd < 0 || !kh) { syz_fuse_kmsg2(tag, fd, kh); syz_fuse_kmsg2(done_tag, 0, fd < 0 ? 1 : 2); return; } if (loops < 1) loops = 1; if (loops > 192) loops = 192; syz_fuse_kmsg2(tag, kh, loops); for (uint32_t i = 0; i < loops; i++) { if (syz_fuse_send_poll_kh(fd, kh) == 0) ok++; else fail++; if ((i & 7) == 7) syz_fuse_sleep_ms(1); } syz_fuse_kmsg2(done_tag, ok, fail); } static pid_t syz_fuse_fork_poll_kh_burst(int fd, uint64_t kh, uint32_t loops, const char* tag, const char* done_tag) { pid_t pid = fork(); if (pid == 0) { syz_fuse_send_poll_kh_burst(fd, kh, loops, tag, done_tag); _exit(0); } return pid; } static void syz_fuse_kill_nowait_log(pid_t* pidp, const char* tag) { if (!pidp || *pidp <= 0) return; int status = 0; int kill_ret = kill(*pidp, SIGKILL); int wait_ret = waitpid(*pidp, &status, WNOHANG); uint64_t ret = ((uint64_t)(kill_ret < 0 ? errno : 0) << 32) | (uint32_t)(wait_ret < 0 ? errno : wait_ret); syz_fuse_kmsg2(tag, *pidp, ret); *pidp = -1; } static void syz_fuse_waitkill_log(pid_t* pidp, const char* tag) { if (!pidp || *pidp <= 0) return; int status = 0; kill(*pidp, SIGKILL); waitpid(*pidp, &status, 0); syz_fuse_kmsg2(tag, *pidp, status); *pidp = -1; } static int syz_fuse_send_store_msg(int fd, uint64_t nodeid, uint64_t offset, uint32_t size) { const uint32_t max_store = 1048576; struct fuse_notify_store_head* head; char* notify; size_t len; ssize_t written; if (!nodeid) nodeid = 2; if (size > max_store) size = max_store; len = sizeof(*head) + size; notify = (char*)malloc(len); if (!notify) return -1; memset(notify, 0, len); head = (struct fuse_notify_store_head*)notify; head->len = len; head->code = FUSE_NOTIFY_STORE_CODE; head->nodeid = nodeid; head->offset = offset; head->size = size; for (uint32_t i = 0; i < size; i++) notify[sizeof(*head) + i] = (char)(0x41 + ((offset + i) & 0xf)); written = write(fd, notify, len); free(notify); if (written == -1) { return -1; } if ((size_t)written != len) { return -1; } return 0; } static int syz_fuse_send_store_msg_splice(int fd, uint64_t nodeid, uint64_t offset, uint32_t size, int use_vmsplice_gift, int disturb_gift_mapping, int split_vmsplice_payload) { const uint32_t max_store = 1048560 - sizeof(struct fuse_notify_store_head); struct fuse_notify_store_head* head; struct fuse_notify_store_head head_buf; char* notify; char* payload = NULL; size_t len; size_t map_len = 0; ssize_t written; ssize_t spliced = -1; int pipefds[2] = {-1, -1}; int used_mmap = 0; int ret = -1; if (!nodeid) nodeid = 2; if (size > max_store) size = max_store; if (use_vmsplice_gift && split_vmsplice_payload && size > 1044480) size = 1044480; len = sizeof(*head) + size; if (use_vmsplice_gift && !split_vmsplice_payload) { if (disturb_gift_mapping) { map_len = (len + 4095) & ~4095; notify = (char*)mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (notify == MAP_FAILED) return -1; used_mmap = 1; } else { notify = (char*)malloc(len); if (!notify) return -1; } memset(notify, 0, len); head = (struct fuse_notify_store_head*)notify; head->len = len; head->code = FUSE_NOTIFY_STORE_CODE; head->nodeid = nodeid; head->offset = offset; head->size = size; for (uint32_t i = 0; i < size; i++) notify[sizeof(*head) + i] = (char)(0x61 + ((offset + i) & 0xf)); if (pipe(pipefds) < 0) goto out_free; (void)fcntl(pipefds[1], F_SETPIPE_SZ, 1048576); struct iovec iov; iov.iov_base = notify; iov.iov_len = len; written = syscall(__NR_vmsplice, pipefds[1], &iov, 1, SPLICE_F_GIFT); syz_fuse_kmsg2("cleanup_store_vmsplice_gift", written < 0 ? -errno : written, len); if (written != (ssize_t)len) { syz_fuse_kmsg2("cleanup_store_splice_pipe_write", written < 0 ? -errno : written, len); goto out_pipe; } if (disturb_gift_mapping && used_mmap) { syz_fuse_kmsg2("cleanup_store_vmsplice_disturb_mapping", len, map_len); (void)mprotect(notify, map_len, PROT_NONE); (void)munmap(notify, map_len); notify = NULL; used_mmap = 0; } spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, len, SPLICE_F_MOVE | SPLICE_F_MORE); if (spliced < 0 && errno == EINVAL) spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, len, 0); syz_fuse_kmsg2("cleanup_store_vmsplice_splice_write", spliced < 0 ? -errno : spliced, len); ret = spliced == (ssize_t)len ? 0 : -1; goto out_pipe; } if (use_vmsplice_gift) { memset(&head_buf, 0, sizeof(head_buf)); head = &head_buf; notify = NULL; if (disturb_gift_mapping) { map_len = ((size ? size : 1) + 4095) & ~4095; payload = (char*)mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (payload == MAP_FAILED) return -1; used_mmap = 1; } else { payload = (char*)malloc(size ? size : 1); if (!payload) return -1; } } else { notify = (char*)malloc(len); if (!notify) return -1; head = (struct fuse_notify_store_head*)notify; } if (notify) memset(notify, 0, len); if (payload) memset(payload, 0, size ? size : 1); head->len = len; head->code = FUSE_NOTIFY_STORE_CODE; head->nodeid = nodeid; head->offset = offset; head->size = size; for (uint32_t i = 0; i < size; i++) { char v = (char)(0x61 + ((offset + i) & 0xf)); if (payload) payload[i] = v; else notify[sizeof(*head) + i] = v; } if (pipe(pipefds) < 0) goto out_free; (void)fcntl(pipefds[1], F_SETPIPE_SZ, 1048576); if (use_vmsplice_gift) { struct iovec iov; ssize_t head_written; head_written = write(pipefds[1], &head_buf, sizeof(head_buf)); syz_fuse_kmsg2("cleanup_store_vmsplice_split_header", head_written < 0 ? -errno : head_written, sizeof(head_buf)); if (head_written != (ssize_t)sizeof(head_buf)) goto out_pipe; iov.iov_base = payload; iov.iov_len = size; written = syscall(__NR_vmsplice, pipefds[1], &iov, 1, SPLICE_F_GIFT); syz_fuse_kmsg2("cleanup_store_vmsplice_gift", written < 0 ? -errno : written, size); } else { written = write(pipefds[1], notify, len); } if (written != (ssize_t)(use_vmsplice_gift ? size : len)) { syz_fuse_kmsg2("cleanup_store_splice_pipe_write", written < 0 ? -errno : written, use_vmsplice_gift ? size : len); goto out_pipe; } if (use_vmsplice_gift && disturb_gift_mapping && used_mmap) { syz_fuse_kmsg2("cleanup_store_vmsplice_disturb_mapping", size, map_len); (void)mprotect(payload, map_len, PROT_NONE); (void)munmap(payload, map_len); payload = NULL; used_mmap = 0; } spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, len, SPLICE_F_MOVE | SPLICE_F_MORE); if (spliced < 0 && errno == EINVAL) spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, len, 0); syz_fuse_kmsg2(use_vmsplice_gift ? "cleanup_store_vmsplice_splice_write" : "cleanup_store_splice_write", spliced < 0 ? -errno : spliced, len); ret = spliced == (ssize_t)len ? 0 : -1; out_pipe: if (pipefds[0] >= 0) close(pipefds[0]); if (pipefds[1] >= 0) close(pipefds[1]); out_free: if (payload) { if (used_mmap) munmap(payload, map_len); else free(payload); } if (notify) { free(notify); } return ret; } static int syz_fuse_send_retrieve_msg(int fd, uint64_t notify_unique, uint64_t nodeid, uint64_t offset, uint32_t size) { struct fuse_notify_retrieve_msg notify; if (!notify_unique) notify_unique = 0xbeef; if (!nodeid) nodeid = 2; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_RETRIEVE_CODE; notify.notify_unique = notify_unique; notify.nodeid = nodeid; notify.offset = offset; notify.size = size; if (write(fd, ¬ify, sizeof(notify)) == -1) { return -1; } return 0; } static void syz_fuse_patch_entry_nodeid(struct fuse_out_header* out_hdr, uint64_t nodeid) { char* payload; if (!out_hdr || !nodeid) return; if (out_hdr->len < sizeof(*out_hdr) + 48) return; payload = (char*)out_hdr + sizeof(*out_hdr); *(uint64_t*)(payload + 0) = nodeid; *(uint64_t*)(payload + 40) = nodeid; } static void syz_fuse_patch_entry_nodeid_mode(struct fuse_out_header* out_hdr, uint64_t nodeid, uint32_t mode, uint64_t size) { char* payload; if (!out_hdr || !nodeid) return; if (out_hdr->len < sizeof(*out_hdr) + 112) return; out_hdr->error = 0; payload = (char*)out_hdr + sizeof(*out_hdr); *(uint64_t*)(payload + 0) = nodeid; *(uint64_t*)(payload + 40) = nodeid; *(uint64_t*)(payload + 48) = size; *(uint32_t*)(payload + 100) = mode; *(uint32_t*)(payload + 104) = (mode & S_IFDIR) ? 2 : 1; } static void syz_fuse_patch_entry_state(struct fuse_out_header* out_hdr, uint64_t nodeid, uint64_t generation, uint32_t mode, uint64_t size, int zero_timeout) { char* payload; syz_fuse_patch_entry_nodeid_mode(out_hdr, nodeid, mode, size); if (!out_hdr || !nodeid) return; if (out_hdr->len < sizeof(*out_hdr) + 112) return; payload = (char*)out_hdr + sizeof(*out_hdr); *(uint64_t*)(payload + 8) = generation; if (zero_timeout) { *(uint64_t*)(payload + 16) = 0; *(uint64_t*)(payload + 24) = 0; *(uint32_t*)(payload + 32) = 0; *(uint32_t*)(payload + 36) = 0; } else { *(uint64_t*)(payload + 16) = 60; *(uint64_t*)(payload + 24) = 60; *(uint32_t*)(payload + 32) = 0; *(uint32_t*)(payload + 36) = 0; } } static void syz_fuse_force_stateful_reply_success(struct fuse_out_header* out_hdr, uint32_t opcode) { if (!out_hdr) return; switch (opcode) { case FUSE_LOOKUP: case FUSE_MKNOD: case FUSE_MKDIR: case FUSE_OPEN: case FUSE_OPENDIR: case FUSE_GETATTR: case FUSE_SETATTR: case FUSE_STATX: case FUSE_READ: case FUSE_READDIR: case FUSE_READDIRPLUS: out_hdr->error = 0; break; } } static int syz_fuse_send_error_response(int fd, const struct fuse_in_header* in_hdr, int err) { struct fuse_out_header out_hdr; memset(&out_hdr, 0, sizeof(out_hdr)); out_hdr.len = sizeof(out_hdr); out_hdr.error = (uint32_t)(-err); out_hdr.unique = in_hdr->unique; if (write(fd, &out_hdr, sizeof(out_hdr)) == -1) { return -1; } return 0; } static int syz_fuse_lookup_name_eq(const char* lookup, size_t lookup_len, const char* name) { size_t name_len; if (!lookup || !name) return 0; name_len = strlen(name); if (lookup_len == name_len && memcmp(lookup, name, name_len) == 0) return 1; if (lookup_len == name_len + 1 && lookup[name_len] == 0 && memcmp(lookup, name, name_len) == 0) return 1; return 0; } static uint64_t syz_fuse_multidentry_lookup_nodeid( const struct fuse_in_header* in_hdr, const char* lookup, size_t lookup_len, const struct syz_fuse_multidentry_req* arg, const char* file_name, const char* file2_name, const char* dir_name, const char* child_name, uint32_t* mode, uint64_t* size) { uint64_t file_nodeid = arg->file_nodeid ? arg->file_nodeid : 2; uint64_t file2_nodeid = arg->file2_nodeid ? arg->file2_nodeid : 3; uint64_t dir_nodeid = arg->dir_nodeid ? arg->dir_nodeid : 4; uint64_t child_nodeid = arg->child_nodeid ? arg->child_nodeid : 5; *mode = S_IFREG | 0644; *size = 65536; if (syz_fuse_lookup_name_eq(lookup, lookup_len, file_name)) return file_nodeid; if (syz_fuse_lookup_name_eq(lookup, lookup_len, file2_name)) return file2_nodeid; if (syz_fuse_lookup_name_eq(lookup, lookup_len, dir_name)) { *mode = S_IFDIR | 0755; *size = 0; return dir_nodeid; } if (in_hdr->nodeid == dir_nodeid && syz_fuse_lookup_name_eq(lookup, lookup_len, child_name)) return child_nodeid; return 0; } static int syz_fuse_handle_one_available_multi( int fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, const struct syz_fuse_multidentry_req* md_arg, const char* file_name, const char* file2_name, const char* dir_name, const char* child_name, int timeout_ms, const char* caller) { struct fuse_out_header* out_hdr = NULL; const struct fuse_in_header* in_hdr; const char* lookup; size_t lookup_len; uint32_t mode; uint64_t size; uint64_t nodeid; int pick; int ret; if (syz_fuse_wait_readable(fd, timeout_ms) < 0) return 1; ret = read(fd, buf, buf_len); if (ret == -1) { syz_fuse_kmsg2("stateflip_read_fail", errno, timeout_ms); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_kmsg2("stateflip_trunc_header", ret, 0); return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_kmsg2("stateflip_trunc_msg", in_hdr->opcode, in_hdr->len); return -1; } syz_fuse_kmsg2("stateflip_seen_opcode", in_hdr->opcode, in_hdr->nodeid); pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick < 0) return -1; if (pick > 0) return 0; if (md_arg && (in_hdr->opcode == FUSE_LOOKUP || in_hdr->opcode == FUSE_MKDIR || in_hdr->opcode == FUSE_MKNOD)) { lookup = (const char*)buf + sizeof(*in_hdr); lookup_len = in_hdr->len - sizeof(*in_hdr); nodeid = syz_fuse_multidentry_lookup_nodeid( in_hdr, lookup, lookup_len, md_arg, file_name, file2_name, dir_name, child_name, &mode, &size); if (nodeid) syz_fuse_patch_entry_nodeid_mode(out_hdr, nodeid, mode, size); } return fuse_send_response(fd, in_hdr, out_hdr); } #define SYZ_FUSE_LOOKUP_HIT_FILE 0x01U #define SYZ_FUSE_LOOKUP_HIT_ALIAS 0x02U #define SYZ_FUSE_LOOKUP_HIT_DIR 0x04U #define SYZ_FUSE_LOOKUP_HIT_CHILD 0x08U #define SYZ_FUSE_LOOKUP_HIT_CHILD2 0x10U #define SYZ_FUSE_LOOKUP_HIT_SUBDIR 0x20U #define SYZ_FUSE_LOOKUP_HIT_SUBCHILD 0x40U struct syz_fuse_stateflip_state { const struct syz_fuse_stateflip_req* arg; const char* file_name; const char* alias_name; const char* dir_name; const char* child_name; const char* child2_name; const char* subdir_name; const char* subchild_name; uint32_t lookup_count; uint32_t lookup_seen_mask; uint32_t file_lookup_hits; uint32_t alias_lookup_hits; uint32_t dir_lookup_hits; uint32_t child_lookup_hits; uint32_t child2_lookup_hits; uint32_t subdir_lookup_hits; uint32_t subchild_lookup_hits; int flipped; int enoent_used; uint64_t captured_notifyreply_unique; uint64_t captured_notifyreply_nodeid; uint32_t captured_notifyreply_len; }; static uint32_t syz_fuse_stateflip_lookup_hit_mask( const struct fuse_in_header* in_hdr, const char* lookup, size_t lookup_len, struct syz_fuse_stateflip_state* st) { const struct syz_fuse_stateflip_req* arg = st->arg; uint64_t dir = arg->dir_nodeid ? arg->dir_nodeid : 4; uint64_t subdir = arg->subdir_nodeid ? arg->subdir_nodeid : 8; if (in_hdr->nodeid == 1) { if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->file_name)) return SYZ_FUSE_LOOKUP_HIT_FILE; if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->alias_name)) return SYZ_FUSE_LOOKUP_HIT_ALIAS; if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->dir_name)) return SYZ_FUSE_LOOKUP_HIT_DIR; } if (in_hdr->nodeid == dir) { if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->child_name)) return SYZ_FUSE_LOOKUP_HIT_CHILD; if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->child2_name)) return SYZ_FUSE_LOOKUP_HIT_CHILD2; if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->subdir_name)) return SYZ_FUSE_LOOKUP_HIT_SUBDIR; } if (in_hdr->nodeid == subdir && syz_fuse_lookup_name_eq(lookup, lookup_len, st->subchild_name)) return SYZ_FUSE_LOOKUP_HIT_SUBCHILD; return 0; } static void syz_fuse_stateflip_note_lookup( const struct fuse_in_header* in_hdr, const char* lookup, size_t lookup_len, struct syz_fuse_stateflip_state* st) { uint32_t mask = syz_fuse_stateflip_lookup_hit_mask(in_hdr, lookup, lookup_len, st); if (!mask) return; st->lookup_seen_mask |= mask; if (mask & SYZ_FUSE_LOOKUP_HIT_FILE) st->file_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_ALIAS) st->alias_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_DIR) st->dir_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_CHILD) st->child_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_CHILD2) st->child2_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_SUBDIR) st->subdir_lookup_hits++; if (mask & SYZ_FUSE_LOOKUP_HIT_SUBCHILD) st->subchild_lookup_hits++; } static uint64_t syz_fuse_stateflip_lookup_nodeid( const struct fuse_in_header* in_hdr, const char* lookup, size_t lookup_len, struct syz_fuse_stateflip_state* st, uint32_t* mode, uint64_t* size, uint64_t* generation) { const struct syz_fuse_stateflip_req* arg = st->arg; uint64_t old_file = arg->old_file_nodeid ? arg->old_file_nodeid : 2; uint64_t new_file = arg->new_file_nodeid ? arg->new_file_nodeid : 6; uint64_t alias = arg->alias_nodeid ? arg->alias_nodeid : old_file; uint64_t dir = arg->dir_nodeid ? arg->dir_nodeid : 4; uint64_t child = arg->child_nodeid ? arg->child_nodeid : 5; uint64_t child2 = arg->child2_nodeid ? arg->child2_nodeid : 7; uint64_t subdir = arg->subdir_nodeid ? arg->subdir_nodeid : 8; uint64_t subchild = arg->subchild_nodeid ? arg->subchild_nodeid : 9; *mode = S_IFREG | 0644; *size = 65536; *generation = st->flipped ? 2 : 1; if (arg->store_size > *size && arg->store_size <= 16777216) *size = arg->store_size; if (arg->retrieve_size > *size && arg->retrieve_size <= 16777216) *size = arg->retrieve_size; if (*size > 65536) syz_fuse_kmsg2("stateflip_entry_big_size", *size, arg->retrieve_size); if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->file_name)) { if (st->flipped && (arg->mode & SYZ_FUSE_SF_FLIP_FILE_TO_DIR)) { *mode = S_IFDIR | 0755; *size = 0; } return st->flipped ? new_file : old_file; } if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->alias_name)) { if (!(arg->mode & SYZ_FUSE_SF_ALIAS_OLD_INODE) && st->flipped) return new_file; return alias; } if (syz_fuse_lookup_name_eq(lookup, lookup_len, st->dir_name)) { if (st->flipped && (arg->mode & SYZ_FUSE_SF_FLIP_DIR_TO_FILE)) { *mode = S_IFREG | 0644; *size = 4096; } else { *mode = S_IFDIR | 0755; *size = 0; } return dir; } if (in_hdr->nodeid == dir && syz_fuse_lookup_name_eq(lookup, lookup_len, st->child_name)) return child; if (in_hdr->nodeid == dir && syz_fuse_lookup_name_eq(lookup, lookup_len, st->child2_name)) return child2; if (in_hdr->nodeid == dir && syz_fuse_lookup_name_eq(lookup, lookup_len, st->subdir_name)) { *mode = S_IFDIR | 0755; *size = 0; return subdir; } if (in_hdr->nodeid == subdir && syz_fuse_lookup_name_eq(lookup, lookup_len, st->subchild_name)) return subchild; return 0; } static void syz_fuse_stateflip_node_attr( struct syz_fuse_stateflip_state* st, uint64_t nodeid, uint32_t* mode, uint64_t* size) { const struct syz_fuse_stateflip_req* arg = st->arg; uint64_t old_file = arg->old_file_nodeid ? arg->old_file_nodeid : 2; uint64_t new_file = arg->new_file_nodeid ? arg->new_file_nodeid : 6; uint64_t alias = arg->alias_nodeid ? arg->alias_nodeid : old_file; uint64_t dir = arg->dir_nodeid ? arg->dir_nodeid : 4; uint64_t child = arg->child_nodeid ? arg->child_nodeid : 5; uint64_t child2 = arg->child2_nodeid ? arg->child2_nodeid : 7; uint64_t subdir = arg->subdir_nodeid ? arg->subdir_nodeid : 8; uint64_t subchild = arg->subchild_nodeid ? arg->subchild_nodeid : 9; *mode = S_IFREG | 0644; *size = 65536; if (arg->store_size > *size && arg->store_size <= 16777216) *size = arg->store_size; if (arg->retrieve_size > *size && arg->retrieve_size <= 16777216) *size = arg->retrieve_size; if (nodeid == dir || nodeid == subdir) { *mode = S_IFDIR | 0755; *size = 0; return; } if (nodeid == old_file || nodeid == new_file || nodeid == alias || nodeid == child || nodeid == child2 || nodeid == subchild) return; } static void syz_fuse_patch_attr_state(struct fuse_out_header* out_hdr, uint64_t nodeid, uint32_t mode, uint64_t size) { char* payload; if (!out_hdr || out_hdr->len < sizeof(*out_hdr) + 104) return; payload = (char*)out_hdr + sizeof(*out_hdr); *(uint64_t*)(payload + 16) = nodeid; *(uint64_t*)(payload + 24) = size; *(uint32_t*)(payload + 76) = mode; *(uint32_t*)(payload + 80) = (mode & S_IFDIR) ? 2 : 1; } static int syz_fuse_stateflip_should_enoent( const struct fuse_in_header* in_hdr, const char* lookup, size_t lookup_len, struct syz_fuse_stateflip_state* st) { if (!st->flipped || st->enoent_used) return 0; if (!(st->arg->mode & SYZ_FUSE_SF_ENOENT_ONCE)) return 0; if (in_hdr->nodeid != 1) return 0; if (!syz_fuse_lookup_name_eq(lookup, lookup_len, st->file_name)) return 0; st->enoent_used = 1; return 1; } static void syz_fuse_kmsg2(const char* tag, long a, long b); static int syz_fuse_send_notifyreply_reply(int fd, uint64_t unique, int error, uint32_t extra_len, const char* tag) { char buf[sizeof(struct fuse_out_header) + 16]; struct fuse_out_header* out_hdr = (struct fuse_out_header*)buf; uint32_t len; ssize_t written; if (fd < 0 || !unique) { syz_fuse_kmsg2(tag, fd, unique ? 1 : 0); return -1; } if (extra_len > 16) extra_len = 16; len = sizeof(*out_hdr) + extra_len; memset(buf, 0xa5, sizeof(buf)); out_hdr->len = len; out_hdr->error = (uint32_t)error; out_hdr->unique = unique; written = write(fd, buf, len); syz_fuse_kmsg2(tag, unique, written < 0 ? -errno : written); return written == (ssize_t)len ? 0 : -1; } static void syz_fuse_reply_captured_notifyreply( int fd, struct syz_fuse_stateflip_state* st, uint64_t flags) { uint64_t unique; uint32_t extra_len = 0; int error = -EIO; int enabled; if (!st) return; unique = st->captured_notifyreply_unique; syz_fuse_kmsg2("cleanup_notifyreply_reply_enter", unique, flags & 0x1fffff); enabled = !!(flags & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_ERROR_REPLY) || !!(flags & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_TRUNC_REPLY); if (!enabled || fd < 0 || !unique) return; if (flags & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_TRUNC_REPLY) { error = 0; extra_len = 1; (void)syz_fuse_send_notifyreply_reply( fd, unique, error, extra_len, "cleanup_notifyreply_trunc_reply"); if (flags & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_DUP_REPLY) (void)syz_fuse_send_notifyreply_reply( fd, unique, error, extra_len, "cleanup_notifyreply_dup_trunc_reply"); } else { (void)syz_fuse_send_notifyreply_reply( fd, unique, error, extra_len, "cleanup_notifyreply_error_reply"); if (flags & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_DUP_REPLY) (void)syz_fuse_send_notifyreply_reply( fd, unique, error, extra_len, "cleanup_notifyreply_dup_error_reply"); } } static int syz_fuse_handle_one_available_stateflip( int fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int timeout_ms, const char* caller) { struct fuse_out_header* out_hdr = NULL; const struct fuse_in_header* in_hdr; const char* lookup; size_t lookup_len; uint32_t mode; uint64_t size; uint64_t generation; uint64_t nodeid; int pick; int ret; if (syz_fuse_wait_readable(fd, timeout_ms) < 0) return 1; ret = read(fd, buf, buf_len); if (ret == -1) { syz_fuse_kmsg2("stateflip_read_fail", errno, timeout_ms); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_kmsg2("stateflip_trunc_header", ret, 0); return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_kmsg2("stateflip_trunc_msg", in_hdr->opcode, in_hdr->len); return -1; } syz_fuse_kmsg2("stateflip_seen_opcode", in_hdr->opcode, in_hdr->nodeid); if (in_hdr->opcode == FUSE_LOOKUP || in_hdr->opcode == FUSE_OPEN || in_hdr->opcode == FUSE_GETATTR || in_hdr->opcode == FUSE_SETATTR || in_hdr->opcode == FUSE_POLL || in_hdr->opcode == FUSE_RELEASE || in_hdr->opcode == FUSE_RELEASEDIR || in_hdr->opcode == FUSE_NOTIFY_REPLY) { syz_fuse_kmsg2("poll_capture_opcode", in_hdr->opcode, in_hdr->nodeid); } if ((st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND) && (st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_RELEASE) && (st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_ONLY) && in_hdr->opcode == FUSE_RELEASE) { syz_fuse_kmsg2("cleanup_capture_release_unreplied", in_hdr->unique, in_hdr->nodeid); return 3; } if ((st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND) && (st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_NOTIFY_REPLY) && in_hdr->opcode == FUSE_NOTIFY_REPLY) { st->captured_notifyreply_unique = in_hdr->unique; st->captured_notifyreply_nodeid = in_hdr->nodeid; st->captured_notifyreply_len = in_hdr->len; syz_fuse_kmsg2(caller, in_hdr->opcode, in_hdr->nodeid); syz_fuse_kmsg2("cleanup_capture_notifyreply_unreplied", in_hdr->unique, in_hdr->nodeid); syz_fuse_reply_captured_notifyreply( fd, st, st->arg->notify_unique); return 4; } if (in_hdr->opcode == FUSE_NOTIFY_REPLY) syz_fuse_kmsg2("cleanup_notifyreply_magic_seen", st->arg->notify_unique & 0x1fffff, st->arg->notify_unique >> 48); pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick < 0) { syz_fuse_kmsg2("stateflip_pick_fail", in_hdr->opcode, in_hdr->nodeid); return -1; } if (pick > 0) return 0; syz_fuse_force_stateful_reply_success(out_hdr, in_hdr->opcode); if (in_hdr->opcode == FUSE_READ && out_hdr == req_out->read) { uint32_t read_size = 4096; char* payload = (char*)out_hdr + sizeof(*out_hdr); if (in_hdr->len >= sizeof(*in_hdr) + 20) read_size = *(uint32_t*)((char*)in_hdr + sizeof(*in_hdr) + 16); if (read_size > 4096) read_size = 4096; if (read_size == 0) read_size = 1; for (uint32_t i = 0; i < read_size; i++) payload[i] = (char)(0x41 + (i & 0x1f)); out_hdr->len = sizeof(*out_hdr) + read_size; syz_fuse_kmsg2("stateflip_read_reply", in_hdr->nodeid, read_size); } if (in_hdr->opcode == FUSE_GETATTR || in_hdr->opcode == FUSE_SETATTR || in_hdr->opcode == FUSE_STATX) { uint32_t attr_mode; uint64_t attr_size; syz_fuse_stateflip_node_attr(st, in_hdr->nodeid, &attr_mode, &attr_size); syz_fuse_patch_attr_state(out_hdr, in_hdr->nodeid, attr_mode, attr_size); syz_fuse_kmsg2("stateflip_attr_node", in_hdr->nodeid, attr_mode); } if (in_hdr->opcode == FUSE_LOOKUP) { lookup = (const char*)buf + sizeof(*in_hdr); lookup_len = in_hdr->len - sizeof(*in_hdr); st->lookup_count++; if (!st->flipped && st->arg->flip_after && st->lookup_count >= st->arg->flip_after) st->flipped = 1; if (syz_fuse_stateflip_should_enoent(in_hdr, lookup, lookup_len, st)) { ret = syz_fuse_send_error_response(fd, in_hdr, ENOENT); if (ret < 0) syz_fuse_kmsg2("stateflip_enoent_send_fail", in_hdr->opcode, errno); return ret; } nodeid = syz_fuse_stateflip_lookup_nodeid(in_hdr, lookup, lookup_len, st, &mode, &size, &generation); if (nodeid) { syz_fuse_stateflip_note_lookup(in_hdr, lookup, lookup_len, st); syz_fuse_patch_entry_state( out_hdr, nodeid, generation, mode, size, !!(st->arg->mode & SYZ_FUSE_SF_ZERO_TIMEOUT)); } } ret = fuse_send_response(fd, in_hdr, out_hdr); if (ret < 0) syz_fuse_kmsg2("stateflip_send_fail", in_hdr->opcode, errno); return ret; } static int syz_fuse_handle_one_available_stateflip_capture_poll( int fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int timeout_ms, uint64_t* poll_kh, const char* caller) { struct fuse_out_header* out_hdr = NULL; const struct fuse_in_header* in_hdr; const char* lookup; size_t lookup_len; uint32_t mode; uint64_t size; uint64_t generation; uint64_t nodeid; int saw_poll = 0; int pick; int ret; if (syz_fuse_wait_readable(fd, timeout_ms) < 0) return 1; ret = read(fd, buf, buf_len); if (ret == -1) { return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { return -1; } if (in_hdr->opcode == FUSE_LOOKUP || in_hdr->opcode == FUSE_OPEN || in_hdr->opcode == FUSE_GETATTR || in_hdr->opcode == FUSE_SETATTR || in_hdr->opcode == FUSE_POLL || in_hdr->opcode == FUSE_RELEASE || in_hdr->opcode == FUSE_RELEASEDIR || in_hdr->opcode == FUSE_NOTIFY_REPLY) syz_fuse_kmsg2("poll_capture_opcode", in_hdr->opcode, in_hdr->nodeid); if ((st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND) && (st->arg->notify_unique & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_NOTIFY_REPLY) && in_hdr->opcode == FUSE_NOTIFY_REPLY) { st->captured_notifyreply_unique = in_hdr->unique; st->captured_notifyreply_nodeid = in_hdr->nodeid; st->captured_notifyreply_len = in_hdr->len; syz_fuse_kmsg2(caller, in_hdr->opcode, in_hdr->nodeid); syz_fuse_kmsg2("cleanup_capture_notifyreply_unreplied", in_hdr->unique, in_hdr->nodeid); syz_fuse_reply_captured_notifyreply( fd, st, st->arg->notify_unique); return 4; } if (in_hdr->opcode == FUSE_NOTIFY_REPLY) syz_fuse_kmsg2("cleanup_notifyreply_magic_seen", st->arg->notify_unique & 0x1fffff, st->arg->notify_unique >> 48); pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick < 0) return -1; if (pick > 0) return 0; syz_fuse_force_stateful_reply_success(out_hdr, in_hdr->opcode); if (in_hdr->opcode == FUSE_LOOKUP) { lookup = (const char*)buf + sizeof(*in_hdr); lookup_len = in_hdr->len - sizeof(*in_hdr); st->lookup_count++; if (!st->flipped && st->arg->flip_after && st->lookup_count >= st->arg->flip_after) st->flipped = 1; if (syz_fuse_stateflip_should_enoent(in_hdr, lookup, lookup_len, st)) return syz_fuse_send_error_response(fd, in_hdr, ENOENT); nodeid = syz_fuse_stateflip_lookup_nodeid(in_hdr, lookup, lookup_len, st, &mode, &size, &generation); if (nodeid) { syz_fuse_stateflip_note_lookup(in_hdr, lookup, lookup_len, st); syz_fuse_patch_entry_state( out_hdr, nodeid, generation, mode, size, !!(st->arg->mode & SYZ_FUSE_SF_ZERO_TIMEOUT)); } } if (in_hdr->opcode == FUSE_POLL && in_hdr->len >= sizeof(*in_hdr) + sizeof(struct fuse_poll_in)) { const struct fuse_poll_in* poll_in = (const struct fuse_poll_in*)(buf + sizeof(*in_hdr)); if (poll_kh) *poll_kh = poll_in->kh; /* * Keep the VFS poll waiter around long enough for the following * FUSE_NOTIFY_POLL to matter. Returning ready events here makes * the child leave poll() before the notify path can race release * and teardown. */ if (out_hdr->len >= sizeof(struct fuse_out_header) + 4) *(uint32_t*)((char*)out_hdr + sizeof(*out_hdr)) = 0; saw_poll = 1; } if (fuse_send_response(fd, in_hdr, out_hdr) == -1) return -1; return saw_poll ? 2 : 0; } static int syz_fuse_handle_one_available(int fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, uint64_t lookup_nodeid, int timeout_ms, const char* caller) { struct fuse_out_header* out_hdr = NULL; const struct fuse_in_header* in_hdr; int pick; int ret; if (syz_fuse_wait_readable(fd, timeout_ms) < 0) return 1; ret = read(fd, buf, buf_len); if (ret == -1) { return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { return -1; } pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick < 0) return -1; if (pick > 0) return 0; if (in_hdr->opcode == FUSE_LOOKUP && lookup_nodeid) syz_fuse_patch_entry_nodeid(out_hdr, lookup_nodeid); return fuse_send_response(fd, in_hdr, out_hdr); } static void syz_fuse_resend_child_op(int file_fd, int op) { char tmp[64]; switch (op & 3) { case 0: if (fsync(file_fd) == -1) break; case 1: if (fdatasync(file_fd) == -1) break; case 2: if (ftruncate(file_fd, 0x1000) == -1) break; default: if (pread(file_fd, tmp, sizeof(tmp), 0) == -1) break; } } static void syz_fuse_make_alt_path(const char* path, char* alt, size_t alt_size) { if (!path || !alt || alt_size == 0) return; snprintf(alt, alt_size, "%s.__syz_alt", path); alt[alt_size - 1] = 0; } static void syz_fuse_expire_child_op(const char* path, int mode) { struct stat st; char alt[512]; int fd; if (mode & 1) fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW); if (mode & 2) { fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) close(fd); } if (!(mode & 3)) { fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) close(fd); } if (mode & 1024) { syz_fuse_make_alt_path(path, alt, sizeof(alt)); if (alt[0]) { rename(path, alt); rename(alt, path); } } if (mode & 2048) unlink(path); if (mode & 4096) { fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600); if (fd >= 0) close(fd); } if (mode & 8192) { syz_fuse_make_alt_path(path, alt, sizeof(alt)); if (alt[0]) { unlink(alt); int ret = link(path, alt); (void)ret; unlink(alt); } } if (mode & 16384) { for (int i = 0; i < 4; i++) { fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW); fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) close(fd); } } } static volatile long syz_fuse_expire_revalidate(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int fuse_fd = (int)a0; int file_fd = (int)a1; const char* path = (const char*)a2; const char* name = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_expire_req* arg = (struct syz_fuse_expire_req*)a7; uint64_t parent; uint64_t old_nodeid; uint64_t lookup_nodeid = 0; int mode; int wait_ms; int requests; char tmp[64]; pid_t pid; if (!req_out || !arg || !path || !name) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } parent = arg->parent ? arg->parent : 1; old_nodeid = arg->old_nodeid ? arg->old_nodeid : 2; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; if (mode & 8) lookup_nodeid = arg->new_nodeid ? arg->new_nodeid : old_nodeid + 1; if (syz_fuse_send_inval_entry(fuse_fd, parent, name, FUSE_EXPIRE_ONLY_FLAG) == -1) return -1; if ((mode & 4) && syz_fuse_send_inc_epoch(fuse_fd) == -1) return -1; pid = fork(); if (pid < 0) { return -1; } if (pid == 0) { syz_fuse_expire_child_op(path, mode); _exit(0); } syz_fuse_sleep_ms(2); requests = (mode & (1024 | 2048 | 4096 | 8192 | 16384)) ? 10 : ((mode & 2) ? 4 : 3); for (int i = 0; i < requests; i++) { int ret = syz_fuse_handle_one_available(fuse_fd, buf, buf_len, req_out, lookup_nodeid, wait_ms, "syz_fuse_expire_revalidate"); if (ret < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (ret > 0) break; } if ((mode & 128) && syz_fuse_send_inval_entry(fuse_fd, parent, name, FUSE_EXPIRE_ONLY_FLAG) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((mode & 256) && syz_fuse_send_inc_epoch(fuse_fd) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (mode & 512) syz_fuse_sleep_ms(10); if ((mode & 16) && syz_fuse_send_delete(fuse_fd, parent, old_nodeid, name) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((mode & 32) && syz_fuse_send_prune_one(fuse_fd, old_nodeid) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (mode & 64) { ssize_t n = pread(file_fd, tmp, sizeof(tmp), 0); (void)n; fsync(file_fd); } syz_fuse_wait_or_kill(pid); return 0; } static volatile long syz_fuse_poll_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6) { int fuse_fd = (int)a0; int file_fd = (int)a1; char* buf = (char*)a2; int buf_len = (int)a3; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a4; int poll_timeout = (int)a6; if (!req_out || !req_out->poll) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } if (poll_timeout < 1) poll_timeout = 1; if (poll_timeout > 300) poll_timeout = 300; pid_t pid = fork(); if (pid < 0) { return -1; } if (pid == 0) { struct pollfd pfd; pfd.fd = file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; poll(&pfd, 1, poll_timeout); _exit(0); } syz_fuse_sleep_ms(2); int ret = read(fuse_fd, buf, buf_len); if (ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->opcode != FUSE_POLL) { syz_fuse_wait_or_kill(pid); return -1; } if (in_hdr->len > (uint32_t)ret || in_hdr->len < sizeof(struct fuse_in_header) + sizeof(struct fuse_poll_in)) { syz_fuse_wait_or_kill(pid); return -1; } const struct fuse_poll_in* poll_in = (const struct fuse_poll_in*)(buf + sizeof(struct fuse_in_header)); uint64_t kh = poll_in->kh; if (fuse_send_response(fuse_fd, in_hdr, req_out->poll) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (a5) { struct fuse_notify_poll_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_POLL_CODE; notify.out.kh = kh; if (write(fuse_fd, ¬ify, sizeof(notify)) == -1) { syz_fuse_wait_or_kill(pid); return -1; } } syz_fuse_wait_or_kill(pid); return 0; } static volatile long syz_fuse_resend_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6) { int fuse_fd = (int)a0; int file_fd = (int)a1; char* buf = (char*)a2; int buf_len = (int)a3; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a4; int wait_timeout = (int)a6; if (!req_out) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } if (wait_timeout < 1) wait_timeout = 1; if (wait_timeout > 500) wait_timeout = 500; pid_t pid = fork(); if (pid < 0) { return -1; } if (pid == 0) { syz_fuse_resend_child_op(file_fd, (int)a5); _exit(0); } if (syz_fuse_wait_readable(fuse_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } int ret = read(fuse_fd, buf, buf_len); if (ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } struct fuse_notify_empty_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_RESEND_CODE; if (write(fuse_fd, ¬ify, sizeof(notify)) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (syz_fuse_wait_readable(fuse_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } ret = read(fuse_fd, buf, buf_len); if (ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } struct fuse_out_header* out_hdr = NULL; int pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, "syz_fuse_resend_notify"); if (pick < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (pick == 0 && fuse_send_response(fuse_fd, in_hdr, out_hdr) == -1) { syz_fuse_wait_or_kill(pid); return -1; } syz_fuse_wait_or_kill(pid); return 0; } static volatile long syz_fuse_clone_resend_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7, volatile long a8) { int req_fd = (int)a0; int notify_fd = (int)a1; int reply_fd = (int)a2; int file_fd = (int)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; int mode = (int)a7; int close_mode = (mode >> 2) & 3; int wait_timeout = (int)a8; if (!req_out) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } if (wait_timeout < 1) wait_timeout = 1; if (wait_timeout > 500) wait_timeout = 500; pid_t pid = fork(); if (pid < 0) { return -1; } if (pid == 0) { syz_fuse_resend_child_op(file_fd, mode & 3); _exit(0); } if (syz_fuse_wait_readable(req_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } int ret = read(req_fd, buf, buf_len); if (ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } struct fuse_notify_empty_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_RESEND_CODE; if (write(notify_fd, ¬ify, sizeof(notify)) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((close_mode & 1) && req_fd != reply_fd) close(req_fd); if ((close_mode & 2) && notify_fd != reply_fd && notify_fd != req_fd) close(notify_fd); if (syz_fuse_wait_readable(reply_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } ret = read(reply_fd, buf, buf_len); if (ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } struct fuse_out_header* out_hdr = NULL; int pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, "syz_fuse_clone_resend_notify"); if (pick < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (pick == 0 && fuse_send_response(reply_fd, in_hdr, out_hdr) == -1) { syz_fuse_wait_or_kill(pid); return -1; } syz_fuse_wait_or_kill(pid); return 0; } static void syz_fuse_store_retrieve_child_op(int file_fd, uint32_t mode, size_t map_len) { char tmp[64]; int active_fd = file_fd; int dup_fd = -1; void* map = MAP_FAILED; void* private_map = MAP_FAILED; if (map_len < 4096) map_len = 4096; if (map_len > 1048576) map_len = 1048576; if (mode & 0x4000) { dup_fd = dup(file_fd); if (dup_fd >= 0) active_fd = dup_fd; } if (mode & (1 | 2 | 4 | 64 | 0x400 | 0x800)) { map = mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_SHARED, active_fd, 0); if (map != MAP_FAILED) { volatile char* p = (volatile char*)map; char v = p[0]; if (mode & 2) p[0] = v ^ 0x5a; } } if ((mode & 0x8000) && map_len >= 4096) { private_map = mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, active_fd, 0); if (private_map != MAP_FAILED) { volatile char* p = (volatile char*)private_map; char v = p[0]; p[map_len - 1] = v ^ 0xa5; } } if (mode & SYZ_FUSE_SR_CHILD_POLL_LOOP) { struct pollfd pfd; pfd.fd = active_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; for (int i = 0; i < 16; i++) { int ret = poll(&pfd, 1, 5); (void)ret; syz_fuse_sleep_ms(1); } } if (mode & 128) syz_fuse_sleep_ms(20); if ((mode & 4) && map != MAP_FAILED) { int ret = msync(map, map_len, MS_SYNC); (void)ret; } if (mode & 8) { ssize_t n = pread(active_fd, tmp, sizeof(tmp), 0); (void)n; } if (mode & 16) { int ret = ftruncate(active_fd, 0); syz_fuse_kmsg2("store_retrieve_child_ftruncate0", ret, ret < 0 ? errno : 0); (void)ret; } if (mode & 32) { int ret = fsync(active_fd); (void)ret; } if ((mode & 64) && map != MAP_FAILED) { int ret = madvise(map, map_len, MADV_DONTNEED); (void)ret; } if ((mode & 0x800) && map != MAP_FAILED) { size_t new_len = map_len <= 32768 ? map_len * 2 : map_len; void* new_map = mremap(map, map_len, new_len, MREMAP_MAYMOVE); if (new_map != MAP_FAILED) { map = new_map; map_len = new_len; ((volatile char*)map)[map_len - 1] ^= 0x33; } } if (mode & 0x1000) { int ret = fallocate(active_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, map_len); syz_fuse_kmsg2("store_retrieve_child_punch_hole", ret, ret < 0 ? errno : 0); (void)ret; ret = fallocate(active_fd, FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, map_len / 2, map_len / 2); syz_fuse_kmsg2("store_retrieve_child_zero_range", ret, ret < 0 ? errno : 0); (void)ret; } if (mode & 0x2000) { int ret = ftruncate(active_fd, (off_t)map_len * 2); syz_fuse_kmsg2("store_retrieve_child_ftruncate_grow", ret, ret < 0 ? errno : 0); (void)ret; ret = ftruncate(active_fd, 1); syz_fuse_kmsg2("store_retrieve_child_ftruncate_shrink", ret, ret < 0 ? errno : 0); (void)ret; ret = ftruncate(active_fd, (off_t)map_len); syz_fuse_kmsg2("store_retrieve_child_ftruncate_restore", ret, ret < 0 ? errno : 0); (void)ret; } if (mode & 512) { ssize_t n = pread(active_fd, tmp, sizeof(tmp), 0); int ret = fsync(active_fd); (void)n; (void)ret; } if (mode & 0x400) syz_fuse_sleep_ms(200); if (mode & SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH) { syz_fuse_sleep_ms(map_len >= 1048576 ? 25 : 250); if (map != MAP_FAILED) { volatile char* p = (volatile char*)map; p[0] ^= 0x11; p[map_len - 1] ^= 0x22; int ret = msync(map, map_len, MS_ASYNC); (void)ret; ret = madvise(map, map_len, MADV_DONTNEED); (void)ret; } ssize_t n = pread(active_fd, tmp, sizeof(tmp), 0); (void)n; int ret = ftruncate(active_fd, 0); syz_fuse_kmsg2("store_retrieve_child_post_ftruncate0", ret, ret < 0 ? errno : 0); (void)ret; ret = ftruncate(active_fd, (off_t)map_len); syz_fuse_kmsg2("store_retrieve_child_post_ftruncate_restore", ret, ret < 0 ? errno : 0); (void)ret; ret = fallocate(active_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, map_len); syz_fuse_kmsg2("store_retrieve_child_post_punch_hole", ret, ret < 0 ? errno : 0); (void)ret; } if (private_map != MAP_FAILED) { int ret = munmap(private_map, map_len); (void)ret; } if (map != MAP_FAILED) { int ret = munmap(map, map_len); (void)ret; } if (dup_fd >= 0) { int ret = close(dup_fd); (void)ret; } } static long syz_fuse_drain_pipe_fd_limited(int fd, int max_loops, const char* tag) { char tmp[4096]; long total = 0; long reads = 0; int empty_spins = 0; for (int i = 0; i < max_loops; i++) { ssize_t n = read(fd, tmp, sizeof(tmp)); if (n > 0) { total += n; reads++; empty_spins = 0; continue; } if (n < 0 && (errno == EAGAIN || errno == EINTR)) { if (++empty_spins > 8) break; syz_fuse_sleep_ms(1); continue; } break; } if (tag) syz_fuse_kmsg2(tag, total, reads); return total; } static void syz_fuse_close_pipe(int pipefds[2]) { if (pipefds[0] >= 0) { int ret = close(pipefds[0]); (void)ret; pipefds[0] = -1; } if (pipefds[1] >= 0) { int ret = close(pipefds[1]); (void)ret; pipefds[1] = -1; } } static int syz_fuse_splice_retrieve_request(int read_fd, int pipefds[2], size_t buf_len, size_t map_len, uint32_t mode, int wait_ms) { size_t splice_len = buf_len; size_t total_spliced = 0; int splice_successes = 0; int last = 0; pipefds[0] = -1; pipefds[1] = -1; if (pipe(pipefds) < 0) return -1; (void)fcntl(pipefds[0], F_SETFL, O_NONBLOCK); (void)fcntl(pipefds[1], F_SETFL, O_NONBLOCK); (void)fcntl(pipefds[1], F_SETPIPE_SZ, 1048576); if (splice_len < FUSE_MIN_READ_BUFFER) splice_len = FUSE_MIN_READ_BUFFER; if (!(mode & SYZ_FUSE_SR_SPLICE_SHORT_RETRIEVE) && splice_len < map_len + FUSE_MIN_READ_BUFFER) splice_len = map_len + FUSE_MIN_READ_BUFFER; if (mode & SYZ_FUSE_SR_SPLICE_SHORT_RETRIEVE) splice_len = FUSE_MIN_READ_BUFFER; if (!(mode & SYZ_FUSE_SR_SPLICE_SHORT_RETRIEVE) && !(mode & (SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN)) && splice_len < 4194304) splice_len = 4194304; if ((mode & (SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN)) && splice_len <= 4194304 && splice_len > 262144) splice_len = 262144; if (splice_len > 16777216 + FUSE_MIN_READ_BUFFER) splice_len = 16777216 + FUSE_MIN_READ_BUFFER; else if (splice_len > 4194304 && buf_len <= 4194304) splice_len = 4194304; (void)fcntl(pipefds[1], F_SETPIPE_SZ, splice_len); syz_fuse_kmsg2("cleanup_notifyreply_splice_pipe_size", fcntl(pipefds[1], F_GETPIPE_SZ), splice_len); for (int i = 0; i < 16; i++) { ssize_t n = syscall(__NR_splice, read_fd, NULL, pipefds[1], NULL, splice_len, SPLICE_F_MOVE | SPLICE_F_MORE | SPLICE_F_NONBLOCK); if (n < 0 && errno == EINVAL) { syz_fuse_sleep_ms(wait_ms); n = syscall(__NR_splice, read_fd, NULL, pipefds[1], NULL, splice_len, 0); } if (n > 0) { if ((size_t)n < FUSE_MIN_READ_BUFFER) { if (splice_len > 4194304 && total_spliced >= 15 * 1048576) { syz_fuse_kmsg2("cleanup_notifyreply_splice_tail_ok", total_spliced, n); return 1; } char drain_buf[256]; ssize_t left = n; while (left > 0) { ssize_t d = read(pipefds[0], drain_buf, left > (ssize_t)sizeof(drain_buf) ? sizeof(drain_buf) : (size_t)left); if (d <= 0) break; left -= d; } syz_fuse_kmsg2("cleanup_notifyreply_splice_drain_small", n, splice_len); syz_fuse_sleep_ms(wait_ms); continue; } if (splice_len > 4194304) { total_spliced += n; splice_successes++; syz_fuse_kmsg2("cleanup_notifyreply_splice_chunk", n, total_spliced); if (total_spliced < 16 * 1048576 && splice_successes < 20) { syz_fuse_sleep_ms(wait_ms > 10 ? 10 : wait_ms); continue; } syz_fuse_kmsg2("cleanup_notifyreply_splice_ok", total_spliced, splice_successes); return 1; } syz_fuse_kmsg2("cleanup_notifyreply_splice_ok", n, splice_len); return 1; } if (n == 0) { last = 0; syz_fuse_sleep_ms(wait_ms); continue; } last = -errno; if (errno == EAGAIN || errno == EINTR) { syz_fuse_sleep_ms(wait_ms); continue; } break; } syz_fuse_kmsg2("cleanup_notifyreply_splice_fail", last, splice_len); return 0; } static void syz_fuse_post_teardown_pipe_ops(int pipefds[2], uint32_t mode) { int dup_pipe[2] = {-1, -1}; int move_pipe[2] = {-1, -1}; int old_flags = -1; if (pipefds[0] < 0) return; old_flags = fcntl(pipefds[0], F_GETFL, 0); if (old_flags >= 0) (void)fcntl(pipefds[0], F_SETFL, old_flags | O_NONBLOCK); syz_fuse_kmsg2("cleanup_pipe_ops_begin", pipefds[0], mode); if (mode & SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN) { if (pipe(dup_pipe) == 0) { ssize_t n; (void)fcntl(dup_pipe[0], F_SETFL, O_NONBLOCK); (void)fcntl(dup_pipe[1], F_SETFL, O_NONBLOCK); (void)fcntl(dup_pipe[1], F_SETPIPE_SZ, 16 * 1048576); n = syscall(__NR_tee, pipefds[0], dup_pipe[1], 16 * 1048576, SPLICE_F_NONBLOCK); syz_fuse_kmsg2("cleanup_pipe_tee_after_teardown", n, n < 0 ? -errno : fcntl(dup_pipe[1], F_GETPIPE_SZ)); (void)syz_fuse_drain_pipe_fd_limited( dup_pipe[0], 512, "cleanup_pipe_tee_drain_after_teardown"); syz_fuse_close_pipe(dup_pipe); } } if (pipe(move_pipe) == 0) { ssize_t n; (void)fcntl(move_pipe[0], F_SETFL, O_NONBLOCK); (void)fcntl(move_pipe[1], F_SETFL, O_NONBLOCK); (void)fcntl(move_pipe[1], F_SETPIPE_SZ, 16 * 1048576); n = syscall(__NR_splice, pipefds[0], NULL, move_pipe[1], NULL, 16 * 1048576, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); syz_fuse_kmsg2("cleanup_pipe_splice_move_after_teardown", n, n < 0 ? -errno : fcntl(move_pipe[1], F_GETPIPE_SZ)); if (n > 0) (void)syz_fuse_drain_pipe_fd_limited( move_pipe[0], 512, "cleanup_pipe_move_drain_after_teardown"); syz_fuse_close_pipe(move_pipe); } if (mode & SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN) (void)syz_fuse_drain_pipe_fd_limited( pipefds[0], 512, "cleanup_pipe_drain_after_teardown"); if (old_flags >= 0) (void)fcntl(pipefds[0], F_SETFL, old_flags); syz_fuse_kmsg2("cleanup_pipe_ops_done", pipefds[0], mode); } static pid_t syz_fuse_fork_hold_pipe_buffers(int pipefds[2], uint32_t mode, int hold_ms, const char* tag) { pid_t pid = fork(); if (pid == 0) { int dup_pipe[2] = {-1, -1}; int move_pipe[2] = {-1, -1}; if (tag) syz_fuse_kmsg2(tag, pipefds[0], hold_ms); if (hold_ms < 1) hold_ms = 1; if (hold_ms > 250) hold_ms = 250; if (pipefds[0] >= 0) { (void)fcntl(pipefds[0], F_SETFL, fcntl(pipefds[0], F_GETFL, 0) | O_NONBLOCK); if ((mode & SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN) && pipe(dup_pipe) == 0) { ssize_t n; (void)fcntl(dup_pipe[0], F_SETFL, O_NONBLOCK); (void)fcntl(dup_pipe[1], F_SETFL, O_NONBLOCK); (void)fcntl(dup_pipe[1], F_SETPIPE_SZ, 16 * 1048576); n = syscall(__NR_tee, pipefds[0], dup_pipe[1], 16 * 1048576, SPLICE_F_NONBLOCK); syz_fuse_kmsg2("cleanup_pipe_hold_tee_after_teardown", n, n < 0 ? -errno : fcntl(dup_pipe[1], F_GETPIPE_SZ)); } if (pipe(move_pipe) == 0) { ssize_t n; (void)fcntl(move_pipe[0], F_SETFL, O_NONBLOCK); (void)fcntl(move_pipe[1], F_SETFL, O_NONBLOCK); (void)fcntl(move_pipe[1], F_SETPIPE_SZ, 16 * 1048576); n = syscall(__NR_splice, pipefds[0], NULL, move_pipe[1], NULL, 16 * 1048576, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); syz_fuse_kmsg2("cleanup_pipe_hold_splice_move_after_teardown", n, n < 0 ? -errno : fcntl(move_pipe[1], F_GETPIPE_SZ)); } syz_fuse_sleep_ms(hold_ms); (void)syz_fuse_drain_pipe_fd_limited( dup_pipe[0], 512, "cleanup_pipe_hold_tee_release_after_delay"); (void)syz_fuse_drain_pipe_fd_limited( move_pipe[0], 512, "cleanup_pipe_hold_move_release_after_delay"); syz_fuse_close_pipe(dup_pipe); syz_fuse_close_pipe(move_pipe); syz_fuse_kmsg2("cleanup_pipe_hold_done_after_delay", pipefds[0], hold_ms); } _exit(0); } return pid; } static volatile long syz_fuse_store_retrieve_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_store_retrieve_req* arg = (struct syz_fuse_store_retrieve_req*)a7; uint64_t nodeid; uint32_t store_size; uint32_t retrieve_size; uint32_t mode; int wait_ms; bool read_fd_closed = false; bool splice_done = false; int retrieve_pipe[2] = {-1, -1}; size_t map_len; pid_t pid; if (!req_out || !arg || !mountpoint) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } nodeid = arg->nodeid ? arg->nodeid : 2; store_size = arg->store_size; retrieve_size = arg->retrieve_size; if (store_size > 1048576) store_size = 1048576; if (retrieve_size > 1048576) retrieve_size = 1048576; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; map_len = store_size > retrieve_size ? store_size : retrieve_size; if (map_len < 4096) map_len = 4096; if (map_len > 65536) map_len = 65536; if (syz_fuse_send_store_msg(notify_fd, nodeid, arg->store_offset, store_size) == -1) return -1; pid = fork(); if (pid < 0) { return -1; } if (pid == 0) { syz_fuse_store_retrieve_child_op(file_fd, mode, map_len); _exit(0); } for (int i = 0; i < 8; i++) { int ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, 0, wait_ms, "syz_fuse_store_retrieve_notify/pre"); if (ret < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (ret > 0) break; } if (mode & 0x4000) { int ret = close(file_fd); (void)ret; } if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, nodeid, arg->retrieve_offset, retrieve_size) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (mode & SYZ_FUSE_SR_SLEEP_BEFORE_ABORT) syz_fuse_sleep_ms(20); if (mode & SYZ_FUSE_SR_SECOND_RETRIEVE_SAME_UNIQUE) { int ret = syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, nodeid, arg->store_offset, store_size); (void)ret; } if (mode & SYZ_FUSE_SR_SECOND_RETRIEVE_INTBIT_UNIQUE) { int ret = syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique | SYZ_FUSE_INT_REQ_BIT, nodeid, arg->store_offset, store_size); (void)ret; } if ((mode & SYZ_FUSE_SR_SPLICE_RETRIEVE_HANDLE) && !read_fd_closed) { int ret = syz_fuse_splice_retrieve_request(read_fd, retrieve_pipe, buf_len, map_len, mode, wait_ms); if (ret > 0) { splice_done = true; } else { syz_fuse_close_pipe(retrieve_pipe); if (mode & SYZ_FUSE_SR_REQUIRE_SPLICE_SUCCESS) { syz_fuse_wait_or_kill(pid); return -1; } } } if (mode & SYZ_FUSE_SR_UMOUNT_AFTER_RETRIEVE) { int ret = umount2(mountpoint, MNT_DETACH); (void)ret; } if (mode & SYZ_FUSE_SR_CLOSE_NOTIFY_AFTER_RETRIEVE) { int ret = close(notify_fd); if (read_fd == notify_fd) read_fd_closed = true; notify_fd = -1; (void)ret; } if ((mode & SYZ_FUSE_SR_CLOSE_READ_AFTER_RETRIEVE) && !read_fd_closed) { int ret = close(read_fd); read_fd_closed = true; (void)ret; } if (!(mode & SYZ_FUSE_SR_SKIP_RETRIEVE_HANDLE) && !splice_done && !read_fd_closed) { for (int i = 0; i < 8; i++) { int ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, 0, wait_ms, "syz_fuse_store_retrieve_notify/retrieve"); if (ret < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (ret > 0) break; } } if ((mode & 0x10000) && notify_fd >= 0 && !read_fd_closed) { if (syz_fuse_send_store_msg(notify_fd, nodeid, arg->retrieve_offset, retrieve_size) == -1) { syz_fuse_wait_or_kill(pid); return -1; } for (int i = 0; i < 4; i++) { int ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, 0, wait_ms, "syz_fuse_store_retrieve_notify/store2"); if (ret < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (ret > 0) break; } } if ((mode & 0x20000) && notify_fd >= 0 && !read_fd_closed) { if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique + 1, nodeid, arg->store_offset, store_size) == -1) { syz_fuse_wait_or_kill(pid); return -1; } for (int i = 0; i < 4; i++) { int ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, 0, wait_ms, "syz_fuse_store_retrieve_notify/retrieve2"); if (ret < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (ret > 0) break; } } if (mode & 256) { int ret = umount2(mountpoint, MNT_DETACH); (void)ret; } syz_fuse_wait_or_kill(pid); syz_fuse_post_teardown_pipe_ops(retrieve_pipe, mode); syz_fuse_close_pipe(retrieve_pipe); return splice_done ? 1 : 0; } static int syz_fuse_faulting_read(int read_fd, const char* mountpoint, size_t read_len, uint32_t mode, int wait_ms) { const size_t page_size = 4096; size_t alloc_len; char* map; int old_flags; pid_t umount_pid = -1; ssize_t n; if (read_len < FUSE_MIN_READ_BUFFER) read_len = FUSE_MIN_READ_BUFFER; if (read_len > 2097152) read_len = 2097152; alloc_len = ((read_len + page_size - 1) & ~(page_size - 1)) + page_size; map = (char*)mmap(NULL, alloc_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (map == MAP_FAILED) return -1; memset(map, 0x5a, alloc_len); old_flags = fcntl(read_fd, F_GETFL, 0); if (old_flags >= 0) (void)fcntl(read_fd, F_SETFL, old_flags | O_NONBLOCK); if (mode & SYZ_FUSE_FR_UMOUNT_DURING_READ) { umount_pid = fork(); if (umount_pid == 0) { syz_fuse_sleep_ms(wait_ms > 10 ? 10 : wait_ms); (void)umount2(mountpoint, MNT_DETACH); _exit(0); } } if (mode & SYZ_FUSE_FR_SHORT_READ) { n = read(read_fd, map, read_len); } else if (mode & SYZ_FUSE_FR_READV_SPLIT) { struct iovec iov[2]; size_t first = page_size; if (read_len <= page_size) read_len = page_size * 2; if (mode & SYZ_FUSE_FR_FAULT_MPROTECT) (void)mprotect(map + page_size, page_size, PROT_NONE); else (void)munmap(map + page_size, page_size); iov[0].iov_base = map; iov[0].iov_len = first; iov[1].iov_base = map + page_size; iov[1].iov_len = read_len - first; n = readv(read_fd, iov, 2); } else { if (mode & SYZ_FUSE_FR_FAULT_MPROTECT) (void)mprotect(map + page_size, page_size, PROT_NONE); else if (mode & SYZ_FUSE_FR_FAULT_MUNMAP) (void)munmap(map + page_size, page_size); n = read(read_fd, map, read_len); } if (mode & SYZ_FUSE_FR_UMOUNT_AFTER_READ) (void)umount2(mountpoint, MNT_DETACH); if (mode & SYZ_FUSE_FR_DRAIN_AFTER_ERROR) { char tmp[FUSE_MIN_READ_BUFFER]; ssize_t drain = read(read_fd, tmp, sizeof(tmp)); (void)drain; } if (umount_pid > 0) syz_fuse_wait_or_kill(umount_pid); if (old_flags >= 0) (void)fcntl(read_fd, F_SETFL, old_flags); (void)munmap(map, alloc_len); return n == -1 ? -errno : (int)n; } static volatile long syz_fuse_fault_retrieve_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_fault_retrieve_req* arg = (struct syz_fuse_fault_retrieve_req*)a7; uint64_t nodeid; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; int wait_ms; pid_t file_pid = -1; int ret; if (!req_out || !arg || !mountpoint || buf_len < FUSE_MIN_READ_BUFFER) return -1; nodeid = arg->nodeid ? arg->nodeid : 2; store_size = arg->store_size; if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size; if (retrieve_size > 1048576) retrieve_size = 1048576; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; if (syz_fuse_send_store_msg(notify_fd, nodeid, arg->store_offset, store_size) == -1) return -1; for (int i = 0; i < 8; i++) { ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, nodeid, wait_ms, "syz_fuse_fault_retrieve_notify/store"); if (ret < 0) return -1; if (ret > 0) break; } if (mode & SYZ_FUSE_FR_FILE_ACTIVITY) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op(file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000, store_size ? store_size : 4096); _exit(0); } } if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, nodeid, arg->retrieve_offset, retrieve_size) == -1) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } if ((mode & SYZ_FUSE_FR_CLOSE_NOTIFY_AFTER_QUEUE) && notify_fd != read_fd) (void)close(notify_fd); if (mode & SYZ_FUSE_FR_SECOND_RETRIEVE) { (void)syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique + 1, nodeid, arg->store_offset, store_size ? store_size : retrieve_size); } if (syz_fuse_wait_readable(read_fd, wait_ms) < 0) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } ret = syz_fuse_faulting_read(read_fd, mountpoint, read_len, mode, wait_ms); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return ret; } static uint32_t syz_fuse_dentry_fault_mode(uint32_t mode) { uint32_t ret = 0; if (mode & SYZ_FUSE_DN_FAULT_MPROTECT) ret |= SYZ_FUSE_FR_FAULT_MPROTECT; if (mode & SYZ_FUSE_DN_FAULT_MUNMAP) ret |= SYZ_FUSE_FR_FAULT_MUNMAP; if (mode & SYZ_FUSE_DN_READV_SPLIT) ret |= SYZ_FUSE_FR_READV_SPLIT; if (mode & SYZ_FUSE_DN_UMOUNT_DURING_READ) ret |= SYZ_FUSE_FR_UMOUNT_DURING_READ; if (mode & SYZ_FUSE_DN_UMOUNT_AFTER_READ) ret |= SYZ_FUSE_FR_UMOUNT_AFTER_READ; if (mode & SYZ_FUSE_DN_DRAIN_AFTER) ret |= SYZ_FUSE_FR_DRAIN_AFTER_ERROR; return ret; } static int syz_fuse_dentry_send_before(int fd, uint64_t parent, uint64_t nodeid, uint64_t child, const char* name, uint32_t mode, uint64_t off, uint32_t size) { if ((mode & SYZ_FUSE_DN_EXPIRE_FIRST) && syz_fuse_send_inval_entry(fd, parent, name, FUSE_EXPIRE_ONLY_FLAG) == -1) return -1; if ((mode & SYZ_FUSE_DN_DELETE_FIRST) && syz_fuse_send_delete(fd, parent, child, name) == -1) return -1; if ((mode & SYZ_FUSE_DN_INVAL_INODE_FIRST) && syz_fuse_send_inval_inode(fd, nodeid, off, size) == -1) return -1; if ((mode & SYZ_FUSE_DN_INC_EPOCH_FIRST) && syz_fuse_send_inc_epoch(fd) == -1) return -1; if ((mode & SYZ_FUSE_DN_PRUNE_FIRST) && syz_fuse_send_prune_one(fd, nodeid) == -1) return -1; return 0; } static int syz_fuse_dentry_send_after(int fd, uint64_t parent, uint64_t nodeid, uint64_t child, const char* name, uint32_t mode, uint64_t off, uint32_t size) { if ((mode & SYZ_FUSE_DN_DELETE_AFTER) && syz_fuse_send_delete(fd, parent, child, name) == -1) return -1; if ((mode & SYZ_FUSE_DN_PRUNE_AFTER) && syz_fuse_send_prune_one(fd, nodeid) == -1) return -1; if ((mode & SYZ_FUSE_DN_EXPIRE_AFTER) && syz_fuse_send_inval_entry(fd, parent, name, FUSE_EXPIRE_ONLY_FLAG) == -1) return -1; if ((mode & SYZ_FUSE_DN_INVAL_INODE_AFTER) && syz_fuse_send_inval_inode(fd, nodeid, off, size) == -1) return -1; if ((mode & SYZ_FUSE_DN_INC_EPOCH_AFTER) && syz_fuse_send_inc_epoch(fd) == -1) return -1; return 0; } static volatile long syz_fuse_dentry_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7, volatile long a8) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; const char* name = (const char*)a4; char* buf = (char*)a5; int buf_len = (int)a6; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a7; struct syz_fuse_dentry_notify_req* arg = (struct syz_fuse_dentry_notify_req*)a8; uint64_t nodeid; uint64_t parent; uint64_t child; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t loops; int wait_ms; pid_t file_pid = -1; int read_fd_closed = 0; int ret = 0; if (!req_out || !arg || !mountpoint || !name || buf_len < FUSE_MIN_READ_BUFFER) return -1; nodeid = arg->nodeid ? arg->nodeid : 2; parent = arg->parent_nodeid ? arg->parent_nodeid : 1; child = arg->child_nodeid ? arg->child_nodeid : nodeid; store_size = arg->store_size; if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size; if (retrieve_size > 1048576) retrieve_size = 1048576; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; loops = arg->loops; if (loops == 0) loops = 4; if (loops > 16) loops = 16; if (mode & SYZ_FUSE_DN_FILE_ACTIVITY) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size ? store_size : 65536); _exit(0); } } if (syz_fuse_dentry_send_before(notify_fd, parent, nodeid, child, name, mode, arg->retrieve_offset, retrieve_size) == -1) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } if (mode & SYZ_FUSE_DN_STORE) { if (syz_fuse_send_store_msg(notify_fd, nodeid, arg->store_offset, store_size) == -1) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } } for (uint32_t i = 0; i < loops; i++) { ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, nodeid, wait_ms, "syz_fuse_dentry_notify/pre"); if (ret < 0) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } if (ret > 0) break; } if (mode & SYZ_FUSE_DN_RETRIEVE) { if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, nodeid, arg->retrieve_offset, retrieve_size) == -1) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } if (mode & SYZ_FUSE_DN_SECOND_RETRIEVE) (void)syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique + 1, nodeid, arg->store_offset, store_size ? store_size : retrieve_size); if ((mode & SYZ_FUSE_DN_CLOSE_NOTIFY_AFTER_QUEUE) && notify_fd != read_fd) { (void)close(notify_fd); notify_fd = -1; } } if (notify_fd >= 0 && syz_fuse_dentry_send_after(notify_fd, parent, nodeid, child, name, mode, arg->retrieve_offset, retrieve_size) == -1) { if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return -1; } if (mode & SYZ_FUSE_DN_RETRIEVE) { if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) { ret = syz_fuse_faulting_read(read_fd, mountpoint, read_len, syz_fuse_dentry_fault_mode(mode), wait_ms); } } else { for (uint32_t i = 0; i < loops; i++) { ret = syz_fuse_handle_one_available(read_fd, buf, buf_len, req_out, nodeid, wait_ms, "syz_fuse_dentry_notify/post"); if (ret < 0) break; if (ret > 0) break; } if (mode & (SYZ_FUSE_DN_FAULT_MPROTECT | SYZ_FUSE_DN_FAULT_MUNMAP | SYZ_FUSE_DN_READV_SPLIT | SYZ_FUSE_DN_UMOUNT_DURING_READ | SYZ_FUSE_DN_UMOUNT_AFTER_READ)) { ret = syz_fuse_faulting_read(read_fd, mountpoint, read_len, syz_fuse_dentry_fault_mode(mode), wait_ms); } } if ((mode & SYZ_FUSE_DN_UMOUNT_AFTER_READ) && !read_fd_closed) (void)umount2(mountpoint, MNT_DETACH); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return ret; } static void syz_fuse_join_path(const char* base, const char* name, char* out, size_t out_size) { if (!out || out_size == 0) return; if (!base || !name) { out[0] = 0; return; } snprintf(out, out_size, "%s/%s", base, name); out[out_size - 1] = 0; } static void syz_fuse_join_path2(const char* base, const char* name1, const char* name2, char* out, size_t out_size) { if (!out || out_size == 0) return; if (!base || !name1 || !name2) { out[0] = 0; return; } snprintf(out, out_size, "%s/%s/%s", base, name1, name2); out[out_size - 1] = 0; } static int syz_fuse_processing_resend_path(int req_fd, int notify_fd, int reply_fd, const char* mountpoint, char* buf, int buf_len, struct syz_fuse_req_out* req_out, int mode, int wait_timeout) { char path[512]; pid_t pid; int rounds = (mode & 0x10) ? 2 : 1; int close_mode = (mode >> 2) & 3; int no_reply = mode & 0x20; int dup_resend = mode & 0x40; int close_reply = mode & 0x80; int unread_resend = mode & 0x100; int skip_notify_reply = mode & 0x200; int target_release = mode & 0x400; int predrain = mode & 0x800; int close_only = mode & 0x1000; int nowait_child = mode & 0x2000; if (req_fd < 0 || notify_fd < 0 || reply_fd < 0 || !mountpoint || !buf || buf_len < FUSE_MIN_READ_BUFFER || !req_out) return -1; if (wait_timeout < 1) wait_timeout = 1; if (wait_timeout > 500) wait_timeout = 500; syz_fuse_join_path(mountpoint, "file", path, sizeof(path)); if (predrain) { int drain_timeout = wait_timeout < 20 ? wait_timeout : 20; if (drain_timeout < 1) drain_timeout = 1; for (int scan = 0; scan < 8; scan++) { struct fuse_in_header* in_hdr; struct fuse_out_header* out_hdr = NULL; int pick; int ret; if (syz_fuse_wait_readable(req_fd, drain_timeout) < 0) break; ret = read(req_fd, buf, buf_len); if (ret == -1 || (size_t)ret < sizeof(struct fuse_in_header)) break; in_hdr = (struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) break; pick = syz_fuse_pick_response( req_out, in_hdr->opcode, &out_hdr, "syz_fuse_processing_resend_path_predrain"); syz_fuse_kmsg2("cleanup_processing_resend_predrain", in_hdr->opcode, pick); if (pick < 0) break; if (pick == 0 && fuse_send_response(req_fd, in_hdr, out_hdr) == -1) break; } } pid = fork(); if (pid < 0) return -1; if (pid == 0) { int fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { if (!close_only) syz_fuse_resend_child_op(fd, mode & 3); close(fd); } _exit(0); } for (int round = 0; round < rounds; round++) { const int final_round = round == rounds - 1; struct fuse_in_header* in_hdr; struct fuse_out_header* out_hdr = NULL; int pick; int ret; int have_target = 0; for (int scan = 0; scan < 8; scan++) { if (syz_fuse_wait_readable(req_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } ret = read(req_fd, buf, buf_len); if (ret == -1 || (size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } in_hdr = (struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } syz_fuse_kmsg2("cleanup_processing_resend_read", in_hdr->opcode, round); if (skip_notify_reply && in_hdr->opcode == FUSE_NOTIFY_REPLY) { syz_fuse_kmsg2( "cleanup_processing_resend_skip_notify_reply", in_hdr->opcode, scan); continue; } if (target_release && in_hdr->opcode != FUSE_RELEASE) { struct fuse_out_header* skip_out = NULL; int skip_pick; skip_pick = syz_fuse_pick_response( req_out, in_hdr->opcode, &skip_out, "syz_fuse_processing_resend_path_skip_release"); syz_fuse_kmsg2( "cleanup_processing_resend_skip_until_release", in_hdr->opcode, skip_pick); if (skip_pick < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (skip_pick == 0 && fuse_send_response(req_fd, in_hdr, skip_out) == -1) { syz_fuse_wait_or_kill(pid); return -1; } continue; } have_target = 1; break; } if (!have_target) { syz_fuse_wait_or_kill(pid); return -1; } if (syz_fuse_send_resend(notify_fd) == -1) { syz_fuse_wait_or_kill(pid); return -1; } if (dup_resend) { int dup_ret = syz_fuse_send_resend(notify_fd); syz_fuse_kmsg2("cleanup_processing_resend_dup_send", dup_ret, round); if (dup_ret == -1) { syz_fuse_wait_or_kill(pid); return -1; } } if (final_round) { if ((close_mode & 1) && req_fd != reply_fd) close(req_fd); if ((close_mode & 2) && notify_fd != reply_fd && notify_fd != req_fd) close(notify_fd); } if (unread_resend && final_round) { syz_fuse_kmsg2("cleanup_processing_resend_unread", close_mode, round); if (close_reply) { close(reply_fd); syz_fuse_kmsg2("cleanup_processing_resend_close_reply", reply_fd, round); } if (nowait_child) syz_fuse_kill_nowait_log( &pid, "cleanup_processing_resend_child_kill_nowait"); else syz_fuse_wait_or_kill(pid); return 0; } if (syz_fuse_wait_readable(reply_fd, wait_timeout) < 0) { syz_fuse_wait_or_kill(pid); return -1; } ret = read(reply_fd, buf, buf_len); if (ret == -1 || (size_t)ret < sizeof(struct fuse_in_header)) { syz_fuse_wait_or_kill(pid); return -1; } in_hdr = (struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { syz_fuse_wait_or_kill(pid); return -1; } syz_fuse_kmsg2("cleanup_processing_resend_resent", in_hdr->opcode, round); if (no_reply && final_round) { syz_fuse_kmsg2("cleanup_processing_resend_no_reply", in_hdr->opcode, round); if (close_reply) { close(reply_fd); syz_fuse_kmsg2("cleanup_processing_resend_close_reply", reply_fd, round); } if (nowait_child) syz_fuse_kill_nowait_log( &pid, "cleanup_processing_resend_child_kill_nowait"); else syz_fuse_wait_or_kill(pid); return 0; } pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, "syz_fuse_processing_resend_path"); if (pick < 0) { syz_fuse_wait_or_kill(pid); return -1; } if (pick == 0 && fuse_send_response(reply_fd, in_hdr, out_hdr) == -1) { syz_fuse_wait_or_kill(pid); return -1; } } if (nowait_child) syz_fuse_kill_nowait_log( &pid, "cleanup_processing_resend_child_kill_nowait"); else syz_fuse_wait_or_kill(pid); return 0; } static void syz_fuse_multidentry_child_op(const char* mountpoint, const char* file_name, const char* file2_name, const char* dir_name, const char* child_name, uint32_t mode, uint32_t loops) { char file_path[512]; char file2_path[512]; char dir_path[512]; char child_path[512]; struct stat st; char tmp[32]; int fd; int dfd; if (loops == 0) loops = 4; if (loops > 16) loops = 16; syz_fuse_join_path(mountpoint, file_name, file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, file2_name, file2_path, sizeof(file2_path)); syz_fuse_join_path(mountpoint, dir_name, dir_path, sizeof(dir_path)); syz_fuse_join_path2(mountpoint, dir_name, child_name, child_path, sizeof(child_path)); for (uint32_t i = 0; i < loops; i++) { (void)fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW); fd = open(file_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { ssize_t n = pread(fd, tmp, sizeof(tmp), 0); (void)n; close(fd); } (void)fstatat(AT_FDCWD, file2_path, &st, AT_SYMLINK_NOFOLLOW); fd = open(file2_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { ssize_t n = pread(fd, tmp, sizeof(tmp), 0); (void)n; close(fd); } (void)fstatat(AT_FDCWD, dir_path, &st, AT_SYMLINK_NOFOLLOW); dfd = open(dir_path, O_RDONLY | O_DIRECTORY | O_NONBLOCK | O_CLOEXEC); if (dfd >= 0) close(dfd); (void)fstatat(AT_FDCWD, child_path, &st, AT_SYMLINK_NOFOLLOW); fd = open(child_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { ssize_t n = pread(fd, tmp, sizeof(tmp), 0); (void)n; close(fd); } if (mode & SYZ_FUSE_MD_RENAME_STORM) { (void)rename(file_path, file2_path); (void)rename(file2_path, file_path); } if (mode & SYZ_FUSE_MD_UNLINK_STORM) { (void)unlink(file2_path); (void)unlink(child_path); } syz_fuse_sleep_ms(1); } } static uint32_t syz_fuse_multidentry_fault_mode(uint32_t mode) { uint32_t ret = 0; if (mode & SYZ_FUSE_MD_FAULT_MPROTECT) ret |= SYZ_FUSE_FR_FAULT_MPROTECT; if (mode & SYZ_FUSE_MD_FAULT_MUNMAP) ret |= SYZ_FUSE_FR_FAULT_MUNMAP; if (mode & SYZ_FUSE_MD_READV_SPLIT) ret |= SYZ_FUSE_FR_READV_SPLIT; if (mode & SYZ_FUSE_MD_DRAIN_AFTER) ret |= SYZ_FUSE_FR_DRAIN_AFTER_ERROR; return ret; } static volatile long syz_fuse_multidentry_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_multidentry_req* arg = (struct syz_fuse_multidentry_req*)a7; const char* file_name = "file"; const char* file2_name = "file2"; const char* dir_name = "dir"; const char* child_name = "a"; uint64_t file_nodeid; uint64_t file2_nodeid; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t retrieve_nodeid; uint32_t mode; uint32_t loops; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; int wait_ms; pid_t child_pid = -1; pid_t file_pid = -1; int ret = 0; if (!req_out || !arg || !mountpoint || buf_len < FUSE_MIN_READ_BUFFER) return -1; file_nodeid = arg->file_nodeid ? arg->file_nodeid : 2; file2_nodeid = arg->file2_nodeid ? arg->file2_nodeid : 3; dir_nodeid = arg->dir_nodeid ? arg->dir_nodeid : 4; child_nodeid = arg->child_nodeid ? arg->child_nodeid : 5; store_size = arg->store_size; if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size; if (retrieve_size > 1048576) retrieve_size = 1048576; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; loops = arg->loops; if (loops == 0) loops = 6; if (loops > 16) loops = 16; child_pid = fork(); if (child_pid == 0) { syz_fuse_multidentry_child_op(mountpoint, file_name, file2_name, dir_name, child_name, mode, loops); _exit(0); } for (uint32_t i = 0; i < loops * 3; i++) { ret = syz_fuse_handle_one_available_multi( read_fd, buf, buf_len, req_out, arg, file_name, file2_name, dir_name, child_name, wait_ms, "syz_fuse_multidentry/pre"); if (ret < 0) goto out; if (ret > 0 && i >= loops) break; } if (mode & SYZ_FUSE_MD_FILE_ACTIVITY) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size ? store_size : 65536); _exit(0); } } if ((mode & SYZ_FUSE_MD_STORE_FILE) && syz_fuse_send_store_msg(notify_fd, file_nodeid, arg->store_offset, store_size) == -1) goto out; if ((mode & SYZ_FUSE_MD_EXPIRE_FILE) && syz_fuse_send_inval_entry(notify_fd, 1, file_name, FUSE_EXPIRE_ONLY_FLAG) == -1) goto out; if ((mode & SYZ_FUSE_MD_INC_EPOCH_BEFORE) && syz_fuse_send_inc_epoch(notify_fd) == -1) goto out; if ((mode & SYZ_FUSE_MD_DELETE_FILE2) && syz_fuse_send_delete(notify_fd, 1, file2_nodeid, file2_name) == -1) goto out; if ((mode & SYZ_FUSE_MD_DELETE_DIR) && syz_fuse_send_delete(notify_fd, 1, dir_nodeid, dir_name) == -1) goto out; if ((mode & SYZ_FUSE_MD_INVAL_CHILD) && syz_fuse_send_inval_inode(notify_fd, child_nodeid, arg->retrieve_offset, retrieve_size) == -1) goto out; if ((mode & SYZ_FUSE_MD_PRUNE_BEFORE) && syz_fuse_send_prune_vec(notify_fd, file_nodeid, file2_nodeid, dir_nodeid, child_nodeid) == -1) goto out; for (uint32_t i = 0; i < loops * 2; i++) { ret = syz_fuse_handle_one_available_multi( read_fd, buf, buf_len, req_out, arg, file_name, file2_name, dir_name, child_name, wait_ms, "syz_fuse_multidentry/mid"); if (ret < 0) goto out; if (ret > 0 && i >= loops / 2) break; } retrieve_nodeid = (mode & SYZ_FUSE_MD_RETRIEVE_CHILD) ? child_nodeid : file_nodeid; if (mode & (SYZ_FUSE_MD_RETRIEVE_FILE | SYZ_FUSE_MD_RETRIEVE_CHILD)) { if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, retrieve_nodeid, arg->retrieve_offset, retrieve_size) == -1) goto out; if ((mode & SYZ_FUSE_MD_CLOSE_NOTIFY_AFTER_QUEUE) && notify_fd != read_fd) { (void)close(notify_fd); notify_fd = -1; } } if (notify_fd >= 0) { if ((mode & SYZ_FUSE_MD_DELETE_FILE_AFTER) && syz_fuse_send_delete(notify_fd, 1, file_nodeid, file_name) == -1) goto out; if ((mode & SYZ_FUSE_MD_PRUNE_AFTER) && syz_fuse_send_prune_vec(notify_fd, file_nodeid, file2_nodeid, dir_nodeid, child_nodeid) == -1) goto out; if ((mode & SYZ_FUSE_MD_INC_EPOCH_AFTER) && syz_fuse_send_inc_epoch(notify_fd) == -1) goto out; } if (mode & (SYZ_FUSE_MD_RETRIEVE_FILE | SYZ_FUSE_MD_RETRIEVE_CHILD)) { if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_multidentry_fault_mode(mode), wait_ms); } else if (mode & (SYZ_FUSE_MD_FAULT_MPROTECT | SYZ_FUSE_MD_FAULT_MUNMAP | SYZ_FUSE_MD_READV_SPLIT)) { if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_multidentry_fault_mode(mode), wait_ms); } out: if (mode & SYZ_FUSE_MD_UMOUNT_AFTER) (void)umount2(mountpoint, MNT_DETACH); if (child_pid > 0) syz_fuse_wait_or_kill(child_pid); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return ret; } static void syz_fuse_stateflip_child_op(const char* mountpoint, uint32_t mode, uint32_t loops) { const char* file_name = "file"; const char* alias_name = "alias"; const char* dir_name = "dir"; const char* child_name = "a"; const char* child2_name = "b"; const char* subdir_name = "sub"; const char* subchild_name = "c"; char file_path[512]; char alias_path[512]; char dir_path[512]; char child_path[512]; char child2_path[512]; char subdir_path[512]; char subchild_path[512]; struct stat st; char tmp[64]; int file_fd = -1; int alias_fd = -1; int dir_fd = -1; int child_fd = -1; int cwd_fd = -1; int cwd_pinned = 0; int dir_cwd_fd = -1; int dir_cwd_pinned = 0; if (loops == 0) loops = 6; if (loops > 24) loops = 24; syz_fuse_join_path(mountpoint, file_name, file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, alias_name, alias_path, sizeof(alias_path)); syz_fuse_join_path(mountpoint, dir_name, dir_path, sizeof(dir_path)); syz_fuse_join_path2(mountpoint, dir_name, child_name, child_path, sizeof(child_path)); syz_fuse_join_path2(mountpoint, dir_name, child2_name, child2_path, sizeof(child2_path)); syz_fuse_join_path2(mountpoint, dir_name, subdir_name, subdir_path, sizeof(subdir_path)); snprintf(subchild_path, sizeof(subchild_path), "%s/%s/%s/%s", mountpoint, dir_name, subdir_name, subchild_name); if ((mode & SYZ_FUSE_SF_CWD_PIN) && chdir(mountpoint) == 0) { cwd_pinned = 1; cwd_fd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC); syz_fuse_kmsg2("stateflip_cwd_pin_root", cwd_fd, cwd_fd < 0 ? errno : 0); } for (uint32_t i = 0; i < loops; i++) { int stat_file_ret = fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_file", stat_file_ret, stat_file_ret < 0 ? errno : 0); if (file_fd < 0) file_fd = open(file_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (file_fd < 0) file_fd = open(file_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (i == 0) syz_fuse_kmsg2("stateflip_open_file", file_fd, file_fd < 0 ? errno : 0); if (file_fd >= 0) { if (mode & SYZ_FUSE_SF_POLL_FILE) { struct pollfd pfd; int pret; pfd.fd = file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; pret = poll(&pfd, 1, 5); syz_fuse_kmsg2("stateflip_poll_file", pret, pret < 0 ? errno : pfd.revents); } ssize_t n = pread(file_fd, tmp, sizeof(tmp), 0); (void)n; } int stat_alias_ret = fstatat(AT_FDCWD, alias_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_alias", stat_alias_ret, stat_alias_ret < 0 ? errno : 0); if (alias_fd < 0) alias_fd = open(alias_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (alias_fd < 0) alias_fd = open(alias_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (i == 0) syz_fuse_kmsg2("stateflip_open_alias", alias_fd, alias_fd < 0 ? errno : 0); if (alias_fd >= 0) { ssize_t n = pread(alias_fd, tmp, sizeof(tmp), 0); (void)n; } int stat_dir_ret = fstatat(AT_FDCWD, dir_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_dir", stat_dir_ret, stat_dir_ret < 0 ? errno : 0); if (dir_fd < 0) dir_fd = open(dir_path, O_RDONLY | O_DIRECTORY | O_NONBLOCK | O_CLOEXEC); if (i == 0) syz_fuse_kmsg2("stateflip_open_dir", dir_fd, dir_fd < 0 ? errno : 0); if (dir_fd >= 0) { (void)fstat(dir_fd, &st); if ((mode & SYZ_FUSE_SF_CWD_PIN) && !dir_cwd_pinned && fchdir(dir_fd) == 0) { dir_cwd_pinned = 1; dir_cwd_fd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC); syz_fuse_kmsg2("stateflip_cwd_pin_dir", dir_cwd_fd, dir_cwd_fd < 0 ? errno : 0); } } int stat_child_ret = fstatat(AT_FDCWD, child_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_child", stat_child_ret, stat_child_ret < 0 ? errno : 0); if (child_fd < 0) child_fd = open(child_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (child_fd < 0) child_fd = open(child_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (i == 0) syz_fuse_kmsg2("stateflip_open_child", child_fd, child_fd < 0 ? errno : 0); if (child_fd >= 0) { ssize_t n = pread(child_fd, tmp, sizeof(tmp), 0); (void)n; } (void)fstatat(AT_FDCWD, child2_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, subdir_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, subchild_path, &st, AT_SYMLINK_NOFOLLOW); if (mode & SYZ_FUSE_SF_RENAME_STORM) { (void)rename(file_path, alias_path); (void)rename(alias_path, file_path); } if (mode & SYZ_FUSE_SF_UNLINK_STORM) { (void)unlink(alias_path); (void)unlink(child2_path); } syz_fuse_sleep_ms(1); } if (mode & SYZ_FUSE_SF_FILE_ACTIVITY) { if (file_fd >= 0) syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000, 65536); if (child_fd >= 0) syz_fuse_store_retrieve_child_op( child_fd, 1 | 8 | 64 | 0x800 | 0x2000, 32768); } if (child_fd >= 0) close(child_fd); if (dir_cwd_fd >= 0) close(dir_cwd_fd); if (cwd_fd >= 0) close(cwd_fd); if (dir_fd >= 0) close(dir_fd); if (alias_fd >= 0) close(alias_fd); if (file_fd >= 0) close(file_fd); } static void syz_fuse_kmsg2(const char* tag, long a, long b) { char msg[160]; int fd; int n; if (!tag) return; fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); if (fd < 0) return; n = snprintf(msg, sizeof(msg), "syz_fuse_notify %s a=%ld b=%ld\n", tag, a, b); if (n > 0) { ssize_t written = write(fd, msg, (size_t)n); (void)written; } close(fd); } static void syz_fuse_mounted_poll_child(const char* mountpoint, uint32_t loops, int wait_ms, uint32_t mode) { char file_path[512]; char alias_path[512]; char child_path[512]; struct stat st; int file_fd = -1; int alias_fd = -1; int child_fd = -1; int logged_open = 0; int logged_poll = 0; if (!mountpoint) return; if (!loops) loops = 16; if (loops > 160) loops = 160; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 100) wait_ms = 100; syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); for (uint32_t i = 0; i < loops; i++) { struct pollfd pfd; (void)fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW); if (file_fd < 0) { file_fd = open(file_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (!logged_open) { syz_fuse_kmsg2("poll_child_open", file_fd, file_fd < 0 ? errno : 0); logged_open = 1; } } if ((mode & SYZ_FUSE_CR_MULTI_PRUNE) && alias_fd < 0) { (void)fstatat(AT_FDCWD, alias_path, &st, AT_SYMLINK_NOFOLLOW); alias_fd = open(alias_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); } if ((mode & SYZ_FUSE_CR_DELETE_STORM) && child_fd < 0) { (void)fstatat(AT_FDCWD, child_path, &st, AT_SYMLINK_NOFOLLOW); child_fd = open(child_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); } if (file_fd >= 0) { int ret; pfd.fd = file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; ret = poll(&pfd, 1, wait_ms); if (!logged_poll) { syz_fuse_kmsg2("poll_child_poll", ret, ret < 0 ? errno : pfd.revents); logged_poll = 1; } } if ((mode & SYZ_FUSE_CR_CLOSE_READ_DURING_READ) && file_fd >= 0 && (i & 7) == 7) { (void)close(file_fd); file_fd = -1; } syz_fuse_sleep_ms(1); } if (child_fd >= 0) close(child_fd); if (alias_fd >= 0) close(alias_fd); if (file_fd >= 0) close(file_fd); } static uint32_t syz_fuse_stateflip_fault_mode(uint32_t mode) { uint32_t ret = 0; if (mode & SYZ_FUSE_SF_FAULT_MPROTECT) ret |= SYZ_FUSE_FR_FAULT_MPROTECT; if (mode & SYZ_FUSE_SF_FAULT_MUNMAP) ret |= SYZ_FUSE_FR_FAULT_MUNMAP; if (mode & SYZ_FUSE_SF_READV_SPLIT) ret |= SYZ_FUSE_FR_READV_SPLIT; return ret; } static volatile long syz_fuse_stateflip_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_stateflip_req* arg = (struct syz_fuse_stateflip_req*)a7; struct syz_fuse_stateflip_state st; uint64_t old_file; uint64_t new_file; uint64_t alias; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t child2_nodeid; uint64_t subdir_nodeid; uint64_t subchild_nodeid; uint32_t mode; uint32_t loops; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; int wait_ms; pid_t child_pid = -1; pid_t file_pid = -1; int ret = 0; if (!req_out || !arg || !mountpoint || buf_len < FUSE_MIN_READ_BUFFER) return -1; old_file = arg->old_file_nodeid ? arg->old_file_nodeid : 2; new_file = arg->new_file_nodeid ? arg->new_file_nodeid : 6; alias = arg->alias_nodeid ? arg->alias_nodeid : old_file; dir_nodeid = arg->dir_nodeid ? arg->dir_nodeid : 4; child_nodeid = arg->child_nodeid ? arg->child_nodeid : 5; child2_nodeid = arg->child2_nodeid ? arg->child2_nodeid : 7; subdir_nodeid = arg->subdir_nodeid ? arg->subdir_nodeid : 8; subchild_nodeid = arg->subchild_nodeid ? arg->subchild_nodeid : 9; store_size = arg->store_size; if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size; if (retrieve_size > 1048576) retrieve_size = 1048576; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; loops = arg->loops; if (loops == 0) loops = 8; if (loops > 24) loops = 24; memset(&st, 0, sizeof(st)); st.arg = arg; st.file_name = "file"; st.alias_name = "alias"; st.dir_name = "dir"; st.child_name = "a"; st.child2_name = "b"; st.subdir_name = "sub"; st.subchild_name = "c"; child_pid = fork(); if (child_pid == 0) { syz_fuse_stateflip_child_op(mountpoint, mode, loops); _exit(0); } for (uint32_t i = 0; i < loops * 4; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_stateflip/pre"); if (ret < 0) goto out; if (ret > 0 && i >= loops) break; } if (mode & SYZ_FUSE_SF_FILE_ACTIVITY) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size ? store_size : 65536); _exit(0); } } if ((mode & SYZ_FUSE_SF_STORE_OLD) && syz_fuse_send_store_msg(notify_fd, old_file, arg->store_offset, store_size) == -1) goto out; if ((mode & SYZ_FUSE_SF_EXPIRE_FILE) && syz_fuse_send_inval_entry(notify_fd, 1, st.file_name, FUSE_EXPIRE_ONLY_FLAG) == -1) goto out; if ((mode & SYZ_FUSE_SF_ALIAS_OLD_INODE) && syz_fuse_send_inval_entry(notify_fd, 1, st.alias_name, FUSE_EXPIRE_ONLY_FLAG) == -1) goto out; if ((mode & SYZ_FUSE_SF_INC_EPOCH) && syz_fuse_send_inc_epoch(notify_fd) == -1) goto out; st.flipped = 1; if ((mode & SYZ_FUSE_SF_DELETE_ALIAS) && syz_fuse_send_delete(notify_fd, 1, alias, st.alias_name) == -1) goto out; if ((mode & SYZ_FUSE_SF_DELETE_DIR) && syz_fuse_send_delete(notify_fd, 1, dir_nodeid, st.dir_name) == -1) goto out; if ((mode & SYZ_FUSE_SF_INVAL_CHILD) && syz_fuse_send_inval_inode(notify_fd, child_nodeid, arg->retrieve_offset, retrieve_size) == -1) goto out; if ((mode & SYZ_FUSE_SF_PRUNE_ALIAS) && syz_fuse_send_prune_vec(notify_fd, old_file, new_file, dir_nodeid, child_nodeid) == -1) goto out; for (uint32_t i = 0; i < loops * 3; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_stateflip/mid"); if (ret < 0) goto out; if (ret > 0 && i >= loops / 2) break; } if ((mode & SYZ_FUSE_SF_DELETE_FILE_AFTER_FLIP) && syz_fuse_send_delete(notify_fd, 1, new_file, st.file_name) == -1) goto out; if ((mode & SYZ_FUSE_SF_PRUNE_AFTER_FLIP) && syz_fuse_send_prune_vec(notify_fd, new_file, alias, subdir_nodeid, subchild_nodeid) == -1) goto out; if ((mode & SYZ_FUSE_SF_RETRIEVE_OLD)) { if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, old_file, arg->retrieve_offset, retrieve_size) == -1) goto out; if ((mode & SYZ_FUSE_SF_CLOSE_NOTIFY_AFTER_RETRIEVE) && notify_fd != read_fd) { (void)close(notify_fd); notify_fd = -1; } } if (mode & (SYZ_FUSE_SF_RETRIEVE_OLD | SYZ_FUSE_SF_FAULT_MPROTECT | SYZ_FUSE_SF_FAULT_MUNMAP | SYZ_FUSE_SF_READV_SPLIT)) { if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_stateflip_fault_mode(mode), wait_ms); } for (uint32_t i = 0; i < loops; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_stateflip/post"); if (ret < 0) goto out; if (ret > 0) break; } out: if (mode & SYZ_FUSE_SF_UMOUNT_AFTER) (void)umount2(mountpoint, MNT_DETACH); if (child_pid > 0) syz_fuse_wait_or_kill(child_pid); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); return ret; } struct syz_fuse_cleanup_race_thread_arg { int notify_fd; int read_fd; const char* mountpoint; const char* name; uint64_t parent; uint64_t nodeid; uint64_t child; uint64_t alt_nodeid; uint64_t notify_unique; uint64_t off; uint32_t size; uint32_t mode; uint32_t wait_ms; uint32_t loops; }; static void* syz_fuse_cleanup_race_thread(void* data) { struct syz_fuse_cleanup_race_thread_arg* arg = (struct syz_fuse_cleanup_race_thread_arg*)data; uint32_t loops = arg->loops; if (loops == 0) loops = 4; if (loops > 96) loops = 96; syz_fuse_sleep_ms(arg->wait_ms > 4 ? arg->wait_ms / 4 : 1); for (uint32_t i = 0; i < loops; i++) { if (arg->notify_fd >= 0) { if (arg->mode & SYZ_FUSE_CR_EXPIRE_STORM) (void)syz_fuse_send_inval_entry( arg->notify_fd, arg->parent, arg->name, FUSE_EXPIRE_ONLY_FLAG); if (arg->mode & SYZ_FUSE_CR_DELETE_STORM) (void)syz_fuse_send_delete( arg->notify_fd, arg->parent, arg->child, arg->name); if (arg->mode & SYZ_FUSE_CR_PRUNE_STORM) { if (arg->mode & SYZ_FUSE_CR_MULTI_PRUNE) (void)syz_fuse_send_prune_vec( arg->notify_fd, arg->nodeid, arg->alt_nodeid, arg->parent, arg->child); else (void)syz_fuse_send_prune_one( arg->notify_fd, arg->nodeid); } if (arg->mode & SYZ_FUSE_CR_INVAL_INODE_STORM) (void)syz_fuse_send_inval_inode( arg->notify_fd, arg->nodeid, arg->off, arg->size); if ((arg->mode & SYZ_FUSE_CR_INVAL_INODE_STORM) && (arg->mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { (void)syz_fuse_send_inval_inode( arg->notify_fd, arg->nodeid, arg->off, 0x7ffffffffffff000ULL); (void)syz_fuse_send_inval_inode( arg->notify_fd, arg->nodeid, arg->off, 0xffffffffffffffffULL); (void)syz_fuse_send_inval_inode( arg->notify_fd, arg->nodeid, 0, 0xffffffffffffffffULL); (void)syz_fuse_send_inval_inode( arg->notify_fd, arg->nodeid, 0, 0x7fffffffffffffffULL); } if (arg->mode & SYZ_FUSE_CR_INC_EPOCH_STORM) (void)syz_fuse_send_inc_epoch(arg->notify_fd); if (arg->notify_unique & SYZ_FUSE_CR_MAGIC_EMPTY_EXTRA) { (void)syz_fuse_send_empty_extra( arg->notify_fd, FUSE_NOTIFY_INC_EPOCH_CODE, 0x1000); (void)syz_fuse_send_empty_extra( arg->notify_fd, FUSE_NOTIFY_RESEND_CODE, 0x1000); } if (arg->mode & SYZ_FUSE_CR_RESEND_STORM) (void)syz_fuse_send_resend(arg->notify_fd); if (arg->mode & SYZ_FUSE_CR_POLL_STORM) (void)syz_fuse_send_poll_kh(arg->notify_fd, (i & 7) + 1); } if ((arg->mode & SYZ_FUSE_CR_UMOUNT_DURING_READ) && i == 0 && arg->mountpoint) (void)umount2(arg->mountpoint, MNT_DETACH); if ((arg->mode & SYZ_FUSE_CR_CLOSE_NOTIFY_DURING_READ) && i == 0 && arg->notify_fd >= 0) { (void)close(arg->notify_fd); arg->notify_fd = -1; } if ((arg->mode & SYZ_FUSE_CR_CLOSE_READ_DURING_READ) && i == 0 && arg->read_fd >= 0) { (void)close(arg->read_fd); arg->read_fd = -1; } syz_fuse_sleep_ms(1); } return NULL; } static void* syz_fuse_edge_valid_race_thread(void* data) { struct syz_fuse_cleanup_race_thread_arg* arg = (struct syz_fuse_cleanup_race_thread_arg*)data; uint32_t loops = arg->loops; uint32_t iters; if (loops == 0) loops = 4; if (loops > 32) loops = 32; iters = 1; syz_fuse_sleep_ms(arg->wait_ms > 8 ? arg->wait_ms / 8 : 1); for (uint32_t i = 0; i < iters; i++) { if (arg->notify_fd >= 0) syz_fuse_send_edge_valid_notify_set( arg->notify_fd, arg->nodeid, arg->alt_nodeid, arg->parent, arg->child, arg->name, arg->off, arg->size, arg->notify_unique + 0x4000 + i); syz_fuse_sleep_ms(1); } syz_fuse_kmsg2("edge_valid_race_thread", iters, loops); return NULL; } static uint32_t syz_fuse_cleanup_race_fault_mode(uint32_t mode) { uint32_t ret = 0; if (mode & SYZ_FUSE_CR_FAULT_MPROTECT) ret |= SYZ_FUSE_FR_FAULT_MPROTECT; if (mode & SYZ_FUSE_CR_FAULT_MUNMAP) ret |= SYZ_FUSE_FR_FAULT_MUNMAP; if (mode & SYZ_FUSE_CR_READV_SPLIT) ret |= SYZ_FUSE_FR_READV_SPLIT; if (mode & SYZ_FUSE_CR_DRAIN_AFTER) ret |= SYZ_FUSE_FR_DRAIN_AFTER_ERROR; return ret; } static void syz_fuse_notify_high_offset_fault_child(const char* mountpoint, int inherited_fd, int inherited_writable, uint64_t off, uint32_t size, uint32_t loops) { char file_path[512]; char tmp[64]; uint64_t base; uint64_t end; size_t map_len; uint32_t rounds; int fd; int writable = 1; if (inherited_fd >= 0) { fd = dup(inherited_fd); writable = inherited_writable; } else { if (!mountpoint) return; syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); fd = open(file_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { fd = open(file_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); writable = 0; } } if (fd < 0) { syz_fuse_kmsg2("cleanup_high_boundary_fault_open_fail", off, errno); return; } if (!size) size = 4096; base = off & ~4095ULL; map_len = (size_t)((off - base) + size + 4095) & ~4095U; if (map_len < 4096) map_len = 4096; if (map_len > 65536) map_len = 65536; rounds = loops ? loops : 8; if (rounds > 64) rounds = 64; syz_fuse_kmsg2("cleanup_high_boundary_fault_start", off, map_len); for (uint32_t i = 0; i < rounds; i++) { void* map; ssize_t n; n = pread(fd, tmp, sizeof(tmp), (off_t)off); (void)n; if (size > 1) { uint64_t last = off + size - 1; if (last >= off) { n = pread(fd, tmp, sizeof(tmp), (off_t)last); (void)n; } } if (writable) { n = pwrite(fd, tmp, sizeof(tmp), (off_t)off); (void)n; } map = mmap(NULL, map_len, writable ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, fd, (off_t)base); if (map != MAP_FAILED) { volatile char* p = (volatile char*)map; size_t delta = (size_t)(off - base); int ret; if (delta < map_len) (void)p[delta]; (void)p[map_len - 1]; if (writable) { if (delta < map_len) p[delta] ^= 0x41; p[map_len - 1] ^= 0x24; } ret = msync(map, map_len, MS_ASYNC); (void)ret; ret = madvise(map, map_len, MADV_DONTNEED); (void)ret; ret = munmap(map, map_len); (void)ret; } if (writable && (i & 3) == 0) { int ret = ftruncate(fd, 0); (void)ret; end = off + size; if (end > off && end < 0x7ffffffffffff000ULL) { ret = ftruncate(fd, (off_t)end); (void)ret; } } syz_fuse_sleep_ms(1); } syz_fuse_kmsg2("cleanup_high_boundary_fault_done", off, rounds); close(fd); } static void syz_fuse_notify_sink_live_hold_child(const char* mountpoint, uint32_t loops, uint32_t fault_mode, size_t map_len); static void syz_fuse_release_resend_fault_window( int read_fd, const char* mountpoint, size_t read_len, uint32_t mode, uint32_t loops, int wait_ms, uint64_t magic, uint32_t store_size) { pid_t fault_pid = -1; uint32_t child_mode; uint32_t fault_loops; size_t map_len; if (!(magic & (SYZ_FUSE_CR_MAGIC_FINAL_POSTPOLL_FAULT | SYZ_FUSE_CR_MAGIC_EARLY_POSTABORT_FAULT | SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT | SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_READ))) return; if (!mountpoint) return; child_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; if (mode & SYZ_FUSE_CR_POLL_STORM) child_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; map_len = store_size > 65536 ? store_size : 65536; fault_loops = loops * 2 + 8; if (magic & SYZ_FUSE_CR_MAGIC_FINAL_POSTPOLL_FAULT) fault_loops += 32; if (magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_READ) fault_loops += loops; if (fault_loops > 128) fault_loops = 128; syz_fuse_kmsg2("cleanup_release_resend_fault_start", read_fd, fault_loops); fault_pid = fork(); if (fault_pid == 0) { syz_fuse_notify_sink_live_hold_child(mountpoint, loops + 96, child_mode, map_len); _exit(0); } if (read_fd >= 0) { for (uint32_t i = 0; i < fault_loops; i++) { (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (fault_pid > 0) syz_fuse_wait_or_kill(fault_pid); syz_fuse_kmsg2("cleanup_release_resend_fault_done", read_fd, fault_pid); } static void syz_fuse_faultread_store_refresh( int notify_fd, int read_fd, const char* mountpoint, uint64_t nodeid, uint64_t store_off, uint64_t retrieve_off, uint32_t store_size, uint32_t retrieve_size, uint64_t notify_unique, uint32_t loops, int wait_ms, uint32_t mode, size_t read_len, const char* tag) { if (notify_fd < 0 || store_size < 1048576 || retrieve_size < 1048576) return; syz_fuse_kmsg2(tag, store_size, retrieve_size); (void)syz_fuse_send_store_msg(notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg(notify_fd, notify_unique + 0x500, nodeid, retrieve_off, retrieve_size); (void)syz_fuse_send_store_msg(notify_fd, nodeid, retrieve_off, retrieve_size); syz_fuse_send_resend_burst( notify_fd, loops / 4 + 2, "cleanup_late_postcopy_faultread_store_refresh_resend", retrieve_size); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops + 4, wait_ms, notify_unique, store_size); } static void syz_fuse_faultread_store_refresh_nowait( int notify_fd, uint64_t nodeid, uint64_t store_off, uint64_t retrieve_off, uint32_t store_size, uint32_t retrieve_size, uint64_t notify_unique, uint32_t loops, const char* tag) { if (notify_fd < 0 || store_size < 1048576 || retrieve_size < 1048576) return; syz_fuse_kmsg2(tag, store_size, retrieve_size); (void)syz_fuse_send_store_msg(notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg(notify_fd, notify_unique + 0x500, nodeid, retrieve_off, retrieve_size); (void)syz_fuse_send_store_msg(notify_fd, nodeid, retrieve_off, retrieve_size); syz_fuse_send_resend_burst( notify_fd, loops / 4 + 2, "cleanup_late_postcopy_faultread_store_refresh_nowait_resend", retrieve_size); } static int syz_fuse_open_mounted_file_with_server( int read_fd, const char* mountpoint, const char* name, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, int* writable) { char path[512]; pid_t server_pid; int status = 0; int fd; int saved_errno = 0; if (writable) *writable = 0; if (read_fd < 0 || !mountpoint || !name || buf_len < FUSE_MIN_READ_BUFFER || !req_out || !st) return -1; syz_fuse_join_path(mountpoint, name, path, sizeof(path)); server_pid = fork(); if (server_pid == 0) { char* srv_buf = (char*)mmap(NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); for (int i = 0; i < 80; i++) { int r = syz_fuse_handle_one_available_stateflip( read_fd, srv_buf, buf_len, req_out, st, wait_ms, "cleanup_open_mounted_file/server"); if (r < 0) break; syz_fuse_sleep_ms(1); } munmap(srv_buf, buf_len); _exit(0); } if (server_pid < 0) return -1; syz_fuse_sleep_ms(2); fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { if (writable) *writable = 1; } else { saved_errno = errno; fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) saved_errno = errno; } syz_fuse_kmsg2("cleanup_open_mounted_file_with_server", fd, fd < 0 ? saved_errno : (writable ? *writable : 0)); kill(server_pid, SIGKILL); waitpid(server_pid, &status, 0); return fd; } static int syz_fuse_handle_until_opcode_stateflip( int fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int timeout_ms, uint32_t target_opcode, uint32_t max_req, const char* caller) { struct fuse_out_header* out_hdr = NULL; if (fd < 0 || !buf || buf_len < FUSE_MIN_READ_BUFFER || !req_out || !st) return -1; if (!max_req) max_req = 1; uint32_t target_hits = 0; for (uint32_t i = 0; i < max_req; i++) { const struct fuse_in_header* in_hdr; int pick; int ret; if (syz_fuse_wait_readable(fd, timeout_ms) < 0) continue; ret = read(fd, buf, buf_len); if (ret < (int)sizeof(struct fuse_in_header)) continue; in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) continue; if (in_hdr->opcode == FUSE_NOTIFY_REPLY || in_hdr->opcode == target_opcode || in_hdr->opcode == FUSE_GETATTR) syz_fuse_kmsg2(caller, in_hdr->opcode, in_hdr->len); pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick < 0) continue; if (pick == 0) { syz_fuse_force_stateful_reply_success(out_hdr, in_hdr->opcode); (void)fuse_send_response(fd, in_hdr, out_hdr); } if (in_hdr->opcode == target_opcode) { target_hits++; syz_fuse_kmsg2("cleanup_handle_until_opcode_target_hit", target_opcode, target_hits); } if ((i & 15) == 15) syz_fuse_sleep_ms(1); } return target_hits ? 0 : 1; } static void syz_fuse_postsplice_parent_fileops( int read_fd, int notify_fd, int file_fd, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, uint32_t retrieve_size, int abort_first, uint64_t poll_kh, uint32_t mode, const char* mountpoint) { pid_t srv_pid = -1; pid_t notify_srv_pid = -1; int srv_wait = wait_ms > 20 ? 20 : wait_ms; int shrink_ok = 0; int ret; if (file_fd < 0 || !req_out || !st || buf_len < FUSE_MIN_READ_BUFFER) return; if (srv_wait < 1) srv_wait = 1; syz_fuse_kmsg2("cleanup_late_postcopy_faultread_parent_postsplice_fileop_begin", file_fd, retrieve_size); syz_fuse_kmsg2("cleanup_late_postcopy_faultread_parent_postsplice_fileop_mode", abort_first, mode); if (read_fd >= 0) { srv_pid = fork(); if (srv_pid == 0) { char* srv_buf = (char*)mmap(NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); (void)syz_fuse_handle_until_opcode_stateflip( read_fd, srv_buf, buf_len, req_out, st, srv_wait, FUSE_SETATTR, 512, "cleanup_parent_postsplice_fileop_server_until_setattr"); munmap(srv_buf, buf_len); _exit(0); } } if (notify_fd >= 0 && notify_fd != read_fd) { notify_srv_pid = fork(); if (notify_srv_pid == 0) { char* srv_buf = (char*)mmap(NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); (void)syz_fuse_handle_until_opcode_stateflip( notify_fd, srv_buf, buf_len, req_out, st, srv_wait, FUSE_SETATTR, 512, "cleanup_parent_postsplice_fileop_notify_server_until_setattr"); munmap(srv_buf, buf_len); _exit(0); } } ret = ftruncate(file_fd, 0); syz_fuse_kmsg2("cleanup_late_postcopy_faultread_parent_postsplice_ftruncate0", ret, ret < 0 ? errno : 0); if (ret == 0 && abort_first) { if (mode & SYZ_FUSE_CR_POLL_STORM) { uint32_t poll_loops = loops * 4 + 16; int close_ret; pid_t poll_close_pid; poll_close_pid = syz_fuse_fork_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_parent_postsplice_pre_abort_during_close_poll", "cleanup_parent_postsplice_pre_abort_during_close_poll_done"); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_abort_during_close_poll_fork", poll_close_pid, poll_kh); syz_fuse_sleep_ms(1); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_abort_existing_fd_close_start", file_fd, poll_kh); close_ret = close(file_fd); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_abort_existing_fd_close", close_ret, close_ret < 0 ? errno : file_fd); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_abort_before_stale", notify_fd, poll_kh); syz_fuse_send_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_parent_postsplice_pre_abort_existing_fd_stale_poll", "cleanup_parent_postsplice_pre_abort_existing_fd_stale_poll_done"); syz_fuse_send_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_late_postcopy_faultread_parent_postsplice_pre_abort_poll", "cleanup_late_postcopy_faultread_parent_postsplice_pre_abort_poll_done"); syz_fuse_kill_nowait_log( &poll_close_pid, "cleanup_parent_postsplice_pre_abort_during_close_poll_kill_nowait"); syz_fuse_kill_nowait_log( &srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_server_kill_nowait"); syz_fuse_kill_nowait_log( ¬ify_srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_notify_server_kill_nowait"); } syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_parent_postsplice_ftruncate0_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_parent_postsplice_ftruncate0_pressure", loops > 4 ? 4 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_ftruncate0_after_pressure", abort_first, loops); } if (ret == 0 && !abort_first) shrink_ok = 1; ret = ftruncate(file_fd, (off_t)retrieve_size); syz_fuse_kmsg2("cleanup_late_postcopy_faultread_parent_postsplice_ftruncate_restore", ret, ret < 0 ? errno : 0); ret = fallocate(file_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, retrieve_size); syz_fuse_kmsg2("cleanup_late_postcopy_faultread_parent_postsplice_punch_hole", ret, ret < 0 ? errno : 0); if (shrink_ok) { if (mode & SYZ_FUSE_CR_POLL_STORM) { uint32_t poll_loops = loops * 4 + 16; int close_ret; pid_t poll_close_pid; poll_close_pid = syz_fuse_fork_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_parent_postsplice_pre_restore_during_close_poll", "cleanup_parent_postsplice_pre_restore_during_close_poll_done"); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_restore_during_close_poll_fork", poll_close_pid, poll_kh); syz_fuse_sleep_ms(1); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_restore_existing_fd_close_start", file_fd, poll_kh); close_ret = close(file_fd); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_restore_existing_fd_close", close_ret, close_ret < 0 ? errno : file_fd); syz_fuse_kmsg2( "cleanup_parent_postsplice_pre_restore_before_stale", notify_fd, poll_kh); syz_fuse_send_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_parent_postsplice_pre_restore_existing_fd_stale_poll", "cleanup_parent_postsplice_pre_restore_existing_fd_stale_poll_done"); syz_fuse_send_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_late_postcopy_faultread_parent_postsplice_pre_restore_abort_poll", "cleanup_late_postcopy_faultread_parent_postsplice_pre_restore_abort_poll_done"); syz_fuse_kill_nowait_log( &poll_close_pid, "cleanup_parent_postsplice_pre_restore_during_close_poll_kill_nowait"); syz_fuse_kill_nowait_log( &srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_server_kill_nowait"); syz_fuse_kill_nowait_log( ¬ify_srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_notify_server_kill_nowait"); } syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_parent_postsplice_restore_then_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_parent_postsplice_restore_then_pressure", loops > 4 ? 4 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_restore_then_after_pressure", abort_first, loops); } syz_fuse_waitkill_log( &srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_server_done"); syz_fuse_waitkill_log( ¬ify_srv_pid, "cleanup_late_postcopy_faultread_parent_postsplice_notify_server_done"); } static pid_t syz_fuse_faultread_store_refresh_child( int notify_fd, const char* mountpoint, int inherited_fd, int read_fd, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint64_t nodeid, uint64_t store_off, uint64_t retrieve_off, uint32_t store_size, uint32_t retrieve_size, uint64_t notify_unique, uint32_t loops) { pid_t pid; uint32_t rounds; if (notify_fd < 0 || store_size < 1048576 || retrieve_size < 1048576) return -1; rounds = loops / 2 + 8; if (rounds < 10) rounds = 10; if (rounds > 32) rounds = 32; syz_fuse_kmsg2("cleanup_late_postcopy_faultread_store_refresh_child_start", rounds, retrieve_size); pid = fork(); if (pid == 0) { char file_path[512]; char alias_path[512]; char child_path[512]; int file_fd = -1; int file_log = 0; int file_errno = 0; if (inherited_fd >= 0) { file_fd = dup(inherited_fd); if (file_fd >= 0) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_file", file_fd, -1); file_log = 1; } } if (mountpoint) { syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); } for (uint32_t i = 0; i < rounds; i++) { if (mountpoint && file_fd < 0) { struct stat st; const char* paths[3] = {file_path, alias_path, child_path}; for (int pi = 0; pi < 3 && file_fd < 0; pi++) { (void)fstatat(AT_FDCWD, paths[pi], &st, AT_SYMLINK_NOFOLLOW); file_fd = open(paths[pi], O_RDWR | O_NONBLOCK | O_CLOEXEC); if (file_fd < 0) file_fd = open(paths[pi], O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (file_fd < 0) { file_errno = errno; } else if (!file_log) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_file", file_fd, pi); file_log = 1; } } } if (file_fd >= 0 && i == 0 && !(notify_unique & 0x800)) { char tmp[64]; int ret; ssize_t n; pid_t srv_pid = -1; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_prefileop_begin", i, retrieve_size); if (read_fd >= 0 && buf_len >= FUSE_MIN_READ_BUFFER && req_out && st) { srv_pid = fork(); if (srv_pid == 0) { int srv_wait = wait_ms > 20 ? 20 : wait_ms; char* srv_buf = (char*)mmap( NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); (void)syz_fuse_handle_until_opcode_stateflip( read_fd, srv_buf, buf_len, req_out, st, srv_wait, FUSE_SETATTR, 256, "cleanup_faultread_prefileop_server_until_setattr"); munmap(srv_buf, buf_len); _exit(0); } } ret = ftruncate(file_fd, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_preftruncate0", ret, ret < 0 ? errno : 0); ret = ftruncate(file_fd, (off_t)retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_preftruncate_restore", ret, ret < 0 ? errno : 0); ret = fallocate(file_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_prepunch_hole", ret, ret < 0 ? errno : 0); if (notify_unique & 0x800) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_prepread_skip", i, retrieve_size); } else { n = pread(file_fd, tmp, sizeof(tmp), 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_prepread", n, n < 0 ? errno : 0); } if (srv_pid > 0) { int status = 0; kill(srv_pid, SIGKILL); waitpid(srv_pid, &status, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_prefileop_server_done", srv_pid, status); } } if ((notify_unique & 0x800) && file_fd >= 0 && (i & 1) == 0) { char tmp[64]; int ret; ssize_t n; pid_t srv_pid = -1; pid_t notify_srv_pid = -1; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_fileop_begin", i, retrieve_size); if (read_fd >= 0 && buf_len >= FUSE_MIN_READ_BUFFER && req_out && st) { srv_pid = fork(); if (srv_pid == 0) { int srv_wait = wait_ms > 20 ? 20 : wait_ms; char* srv_buf = (char*)mmap( NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); for (int si = 0; si < 32; si++) { int rr = syz_fuse_handle_one_available_stateflip( read_fd, srv_buf, buf_len, req_out, st, srv_wait, "cleanup_faultread_presend_fileop_server"); if (rr < 0) break; syz_fuse_sleep_ms(1); } munmap(srv_buf, buf_len); _exit(0); } } if (notify_fd >= 0 && notify_fd != read_fd && buf_len >= FUSE_MIN_READ_BUFFER && req_out && st) { notify_srv_pid = fork(); if (notify_srv_pid == 0) { int srv_wait = wait_ms > 20 ? 20 : wait_ms; char* srv_buf = (char*)mmap( NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); for (int si = 0; si < 32; si++) { int rr = syz_fuse_handle_one_available_stateflip( notify_fd, srv_buf, buf_len, req_out, st, srv_wait, "cleanup_faultread_presend_fileop_notify_server"); if (rr < 0) break; syz_fuse_sleep_ms(1); } munmap(srv_buf, buf_len); _exit(0); } } ret = ftruncate(file_fd, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_ftruncate0", ret, ret < 0 ? errno : 0); if ((notify_unique & 0x800) && ret == 0) { syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_child_presend_ftruncate0_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_child_presend_ftruncate0_pressure", loops); } ret = ftruncate(file_fd, (off_t)retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_ftruncate_restore", ret, ret < 0 ? errno : 0); ret = fallocate(file_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_punch_hole", ret, ret < 0 ? errno : 0); if (notify_unique & 0x800) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_io_skip", i, retrieve_size); } else { n = pread(file_fd, tmp, sizeof(tmp), 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_pread", n, n < 0 ? errno : 0); ret = fsync(file_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_fsync", ret, ret < 0 ? errno : 0); } if (srv_pid > 0) { int status = 0; kill(srv_pid, SIGKILL); waitpid(srv_pid, &status, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_fileop_server_done", srv_pid, status); } if (notify_srv_pid > 0) { int status = 0; kill(notify_srv_pid, SIGKILL); waitpid(notify_srv_pid, &status, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_presend_fileop_notify_server_done", notify_srv_pid, status); } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_presend_fileop", i, retrieve_size); } (void)syz_fuse_send_store_msg(notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_unique + 0x700 + i, nodeid, retrieve_off, retrieve_size); if ((i & 1) == 0) (void)syz_fuse_send_store_msg(notify_fd, nodeid, retrieve_off, retrieve_size); if (file_fd >= 0 && (i & 1) == 0) { char tmp[64]; int ret; ssize_t n; pid_t srv_pid = -1; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_fileop_begin", i, retrieve_size); if (read_fd >= 0 && buf_len >= FUSE_MIN_READ_BUFFER && req_out && st) { srv_pid = fork(); if (srv_pid == 0) { int srv_wait = wait_ms > 20 ? 20 : wait_ms; char* srv_buf = (char*)mmap( NULL, buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (srv_buf == MAP_FAILED) _exit(1); for (int si = 0; si < 24; si++) { int rr = syz_fuse_handle_one_available_stateflip( read_fd, srv_buf, buf_len, req_out, st, srv_wait, "cleanup_faultread_fileop_server"); if (rr < 0) break; syz_fuse_sleep_ms(1); } munmap(srv_buf, buf_len); _exit(0); } } ret = ftruncate(file_fd, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_ftruncate0", ret, ret < 0 ? errno : 0); ret = ftruncate(file_fd, (off_t)retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_ftruncate_restore", ret, ret < 0 ? errno : 0); ret = fallocate(file_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_punch_hole", ret, ret < 0 ? errno : 0); n = pread(file_fd, tmp, sizeof(tmp), 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_pread", n, n < 0 ? errno : 0); ret = fsync(file_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_fsync", ret, ret < 0 ? errno : 0); if (srv_pid > 0) { int status = 0; kill(srv_pid, SIGKILL); waitpid(srv_pid, &status, 0); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_child_fileop_server_done", srv_pid, status); } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_fileop", i, retrieve_size); } syz_fuse_sleep_ms(1); } if (!file_log) syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_file_fail", -1, file_errno); if (file_fd >= 0) close(file_fd); _exit(0); } return pid; } static volatile long syz_fuse_notify_cleanup_race(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7, volatile long a8) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; const char* name = (const char*)a4; char* buf = (char*)a5; int buf_len = (int)a6; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a7; struct syz_fuse_cleanup_race_req* arg = (struct syz_fuse_cleanup_race_req*)a8; struct syz_fuse_cleanup_race_thread_arg thr_arg; struct syz_fuse_fallback_req_out fallback_req; struct syz_fuse_stateflip_req sf_arg; struct syz_fuse_stateflip_state st; pthread_t thr; pthread_t edge_thr; int have_thread = 0; int have_edge_thread = 0; pid_t file_pid = -1; pid_t materialize_pid = -1; pid_t live_hold_pid = -1; pid_t poll_pid = -1; pid_t poststore_fault_pid = -1; pid_t postabort_fault_pid = -1; int orig_read_fd = read_fd; uint64_t nodeid; uint64_t parent; uint64_t child; uint64_t alt_nodeid; uint64_t poll_kh = 0; uint64_t notify_magic; uint64_t store_off; uint64_t retrieve_off; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t loops; uint32_t live_fault_mode; int store_splice_write; int store_vmsplice_gift; int store_vmsplice_disturb; int store_vmsplice_split; int use_stateful; int poll_seen = 0; int clone_fd = -1; int high_pinned_fd = -1; int high_pinned_writable = 0; int notifyreply_pipe[2] = {-1, -1}; int notifyreply_splice_done = 0; int wait_ms; int raw_wait_ms; int ret = 0; if (!arg || !mountpoint || !name || buf_len < FUSE_MIN_READ_BUFFER) return -1; notify_magic = arg->notify_unique; if (!req_out) { syz_fuse_prepare_fallback_req_out(&fallback_req); req_out = &fallback_req.req; } nodeid = arg->nodeid ? arg->nodeid : 2; parent = arg->parent_nodeid ? arg->parent_nodeid : 1; child = arg->child_nodeid ? arg->child_nodeid : nodeid; alt_nodeid = arg->alt_nodeid ? arg->alt_nodeid : nodeid + 1; store_off = arg->store_offset; retrieve_off = arg->retrieve_offset ? arg->retrieve_offset : store_off; store_splice_write = !!(arg->store_size & 0x80000000U); store_vmsplice_gift = !!(arg->store_size & 0x40000000U); store_vmsplice_disturb = !!(arg->store_size & 0x20000000U); store_vmsplice_split = !!(arg->store_size & 0x10000000U); store_size = arg->store_size ? (arg->store_size & 0x0fffffffU) : 4096; #define SYZ_FUSE_CLEANUP_SEND_STORE(fd_,nodeid_,off_,size_) (store_splice_write ? syz_fuse_send_store_msg_splice((fd_), (nodeid_), (off_), (size_), store_vmsplice_gift, store_vmsplice_disturb, store_vmsplice_split) : syz_fuse_send_store_msg((fd_), (nodeid_), (off_), (size_))) if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size ? arg->retrieve_size : store_size; if (retrieve_size > 16777216) retrieve_size = 16777216; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) syz_fuse_kmsg2("cleanup_arg_notify_unique_low", (uint32_t)notify_magic, (uint32_t)(notify_magic >> 32)); if (mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) { if (!store_off || store_off < 0x7fffffffffffe000ULL) store_off = 0x7ffffffffffff000ULL; if (!retrieve_off || retrieve_off < 0x7fffffffffffe000ULL) retrieve_off = store_off; if (store_size < 0x1001) store_size = 0x1001; if (retrieve_size < store_size) retrieve_size = store_size; } raw_wait_ms = arg->wait_ms; wait_ms = raw_wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; loops = arg->loops; if (loops == 0) loops = 6; if (loops > 32) loops = 32; use_stateful = !!(mode & (SYZ_FUSE_CR_MATERIALIZE_STATEFUL | SYZ_FUSE_CR_LIVE_HOLD_FAULTS)); if (mode & (SYZ_FUSE_CR_CLONE_NOTIFY_FD | SYZ_FUSE_CR_CLONE_READ_FD)) { int oldfd = read_fd >= 0 ? read_fd : notify_fd; clone_fd = open("/dev/fuse", O_RDWR | O_CLOEXEC); if (clone_fd >= 0 && ioctl(clone_fd, FUSE_DEV_IOC_CLONE, &oldfd) == 0) { if (mode & SYZ_FUSE_CR_CLONE_READ_FD) read_fd = clone_fd; else notify_fd = clone_fd; syz_fuse_kmsg2("cleanup_clone_ok", clone_fd, (mode & SYZ_FUSE_CR_CLONE_READ_FD) ? 1 : 0); } else { syz_fuse_kmsg2("cleanup_clone_fail", clone_fd, errno); if (clone_fd >= 0) { (void)close(clone_fd); clone_fd = -1; } } } if (use_stateful) { memset(&sf_arg, 0, sizeof(sf_arg)); sf_arg.notify_unique = notify_magic; sf_arg.old_file_nodeid = nodeid; sf_arg.new_file_nodeid = alt_nodeid; sf_arg.alias_nodeid = nodeid; sf_arg.dir_nodeid = parent == 1 ? 4 : parent; sf_arg.child_nodeid = child == nodeid ? 5 : child; sf_arg.child2_nodeid = 7; sf_arg.subdir_nodeid = 8; sf_arg.subchild_nodeid = 9; sf_arg.store_offset = store_off; sf_arg.retrieve_offset = retrieve_off; sf_arg.store_size = store_size; sf_arg.retrieve_size = retrieve_size; sf_arg.read_len = read_len; sf_arg.mode = SYZ_FUSE_SF_ALIAS_OLD_INODE | SYZ_FUSE_SF_FILE_ACTIVITY; if (mode & SYZ_FUSE_CR_POLL_STORM) sf_arg.mode |= SYZ_FUSE_SF_POLL_FILE; if (mode & SYZ_FUSE_CR_ZERO_TIMEOUT_MATERIALIZE) sf_arg.mode |= SYZ_FUSE_SF_ZERO_TIMEOUT; if (notify_magic & SYZ_FUSE_CR_MAGIC_CWD_PIN) sf_arg.mode |= SYZ_FUSE_SF_CWD_PIN; sf_arg.wait_ms = wait_ms; sf_arg.loops = loops + 4; memset(&st, 0, sizeof(st)); st.arg = &sf_arg; st.file_name = "file"; st.alias_name = "alias"; st.dir_name = "dir"; st.child_name = "a"; st.child2_name = "b"; st.subdir_name = "sub"; st.subchild_name = "c"; materialize_pid = fork(); if (materialize_pid == 0) { syz_fuse_stateflip_child_op( mountpoint, sf_arg.mode, loops + 4); _exit(0); } for (uint32_t i = 0; i < loops * 8; i++) { if (mode & SYZ_FUSE_CR_POLL_STORM) { ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, wait_ms, &poll_kh, "syz_fuse_cleanup/materialize-poll"); } else { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_cleanup/materialize"); } if (ret < 0) goto cleanup; if (ret == 3 || ret == 4) { struct fuse_in_header* rel_hdr; struct fuse_out_header* out_hdr = NULL; int pick; int rr; int captured_notify_reply = (ret == 4); syz_fuse_kmsg2( "cleanup_release_processing_resend_captured", read_fd, notify_fd); if (captured_notify_reply) { syz_fuse_kmsg2( "cleanup_notifyreply_processing_resend_captured", read_fd, notify_fd); } if (captured_notify_reply && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY)) { syz_fuse_reply_captured_notifyreply( read_fd, &st, notify_magic); if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); syz_fuse_kmsg2( "cleanup_notifyreply_processing_no_resend", read_fd, notify_fd); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2( "cleanup_notifyreply_processing_abort", read_fd, notify_fd); syz_fuse_abort_all_connections_now( "cleanup_notifyreply_processing_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_notifyreply_processing_pressure", loops); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT)) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_notifyreply_processing_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_notifyreply_processing_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } goto cleanup; } if (syz_fuse_send_resend(notify_fd) == -1) goto cleanup; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_DUP) (void)syz_fuse_send_resend(notify_fd); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_UNREAD)) { if (syz_fuse_wait_readable(read_fd, wait_ms) < 0) goto cleanup; rr = read(read_fd, buf, buf_len); if (rr == -1 || (size_t)rr < sizeof(struct fuse_in_header)) goto cleanup; rel_hdr = (struct fuse_in_header*)buf; if (rel_hdr->len > (uint32_t)rr) goto cleanup; syz_fuse_kmsg2( "cleanup_release_processing_resend_resent", rel_hdr->opcode, rel_hdr->nodeid); if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_NOREPLY) { if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); syz_fuse_kmsg2( "cleanup_release_processing_resend_no_reply", rel_hdr->opcode, rel_hdr->nodeid); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2( "cleanup_release_resend_abort", read_fd, notify_fd); syz_fuse_abort_all_connections_now( "cleanup_release_resend_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_release_resend_pressure", loops); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT)) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_release_resend_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_release_resend_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } goto cleanup; } pick = syz_fuse_pick_response( req_out, rel_hdr->opcode, &out_hdr, "syz_fuse_release_resend_direct"); if (pick < 0) goto cleanup; if (pick == 0 && fuse_send_response(read_fd, rel_hdr, out_hdr) == -1) goto cleanup; } else { if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); syz_fuse_kmsg2( "cleanup_release_processing_resend_unread", read_fd, notify_fd); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2( "cleanup_release_resend_unread_abort", read_fd, notify_fd); syz_fuse_abort_all_connections_now( "cleanup_release_resend_unread_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_release_resend_unread_pressure", loops); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT)) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_release_resend_unread_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_release_resend_unread_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } } goto cleanup; } if (ret == 2) { poll_seen = 1; if (notify_fd >= 0 && poll_kh) { for (uint32_t j = 0; j < loops * 2; j++) (void)syz_fuse_send_poll_kh( notify_fd, poll_kh); } } if (ret > 0 && i >= loops) break; } } if ((mode & SYZ_FUSE_CR_STORE_FIRST) && (store_splice_write ? syz_fuse_send_store_msg_splice(notify_fd, nodeid, store_off, store_size, store_vmsplice_gift, store_vmsplice_disturb, store_vmsplice_split) : syz_fuse_send_store_msg(notify_fd, nodeid, store_off, store_size)) == -1) { ret = -1; goto cleanup; } for (uint32_t i = 0; i < loops; i++) { if (use_stateful) ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_cleanup/pre-stateful"); else ret = syz_fuse_handle_one_available( read_fd, buf, buf_len, req_out, nodeid, wait_ms, "syz_fuse_cleanup/pre"); if (ret < 0) goto cleanup; if (ret > 0) break; } if ((mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) && high_pinned_fd < 0 && mountpoint && name) { char pin_path[512]; int pin_errno = 0; syz_fuse_join_path(mountpoint, name, pin_path, sizeof(pin_path)); high_pinned_fd = open(pin_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (high_pinned_fd >= 0) { high_pinned_writable = 1; } else { pin_errno = errno; high_pinned_fd = open(pin_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (high_pinned_fd >= 0) high_pinned_writable = 0; } syz_fuse_kmsg2("cleanup_high_boundary_pin_fd", high_pinned_fd, high_pinned_fd < 0 ? pin_errno : high_pinned_writable); } if (!(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) && store_size >= 1048576 && retrieve_size >= 1048576 && high_pinned_fd < 0 && use_stateful && mountpoint && name) { high_pinned_fd = syz_fuse_open_mounted_file_with_server( read_fd, mountpoint, name, buf_len, req_out, &st, wait_ms, &high_pinned_writable); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_ONLY) && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_RELEASE) && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND) && read_fd >= 0 && notify_fd >= 0 && mountpoint) { int release_mode = (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_ONESHOT) ? 0x7 : 0x17; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_NOREPLY) release_mode |= 0x20; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_DUP) release_mode |= 0x40; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) release_mode |= 0x80; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_UNREAD) release_mode |= 0x100; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) release_mode |= 0x8; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY) release_mode |= 0x200; release_mode |= 0x400 | 0x1000; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_PREDRAIN) release_mode |= 0x800; if (notify_magic & SYZ_FUSE_CR_MAGIC_RELEASE_RESEND_CONTINUE) release_mode |= 0x2000; syz_fuse_kmsg2("cleanup_release_processing_resend_start", read_fd, notify_fd); (void)syz_fuse_processing_resend_path( read_fd, notify_fd, read_fd, mountpoint, buf, buf_len, req_out, release_mode, wait_ms); syz_fuse_kmsg2("cleanup_release_processing_resend_done", read_fd, notify_fd); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_RELEASE_RESEND_CONTINUE)) goto cleanup; syz_fuse_kmsg2("cleanup_release_processing_resend_continue", read_fd, notify_fd); } if ((mode & SYZ_FUSE_CR_FILE_ACTIVITY) && file_fd >= 0) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size ? store_size : 65536); _exit(0); } } if (mode & SYZ_FUSE_CR_LIVE_HOLD_FAULTS) { live_fault_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; if (mode & SYZ_FUSE_CR_POLL_STORM) live_fault_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; live_hold_pid = fork(); if (live_hold_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 96, live_fault_mode, store_size > 65536 ? store_size : 65536); _exit(0); } } if ((mode & SYZ_FUSE_CR_POLL_STORM) && use_stateful && !poll_seen) { uint32_t poll_limit = loops * 4 + 32; int poll_wait = wait_ms > 10 ? 10 : wait_ms; if (poll_limit > 128) poll_limit = 128; poll_pid = fork(); if (poll_pid == 0) { syz_fuse_mounted_poll_child(mountpoint, loops + 64, wait_ms, mode); _exit(0); } for (uint32_t i = 0; i < poll_limit; i++) { ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, poll_wait, &poll_kh, "syz_fuse_cleanup/poll-capture"); if (ret < 0) { goto cleanup; } if (ret == 4 && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY)) { syz_fuse_kmsg2( "cleanup_poll_notifyreply_processing_no_resend", read_fd, notify_fd); syz_fuse_reply_captured_notifyreply( read_fd, &st, notify_magic); if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2( "cleanup_poll_notifyreply_processing_abort", read_fd, notify_fd); syz_fuse_abort_all_connections_now( "cleanup_poll_notifyreply_processing_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_poll_notifyreply_processing_pressure", loops); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT)) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_poll_notifyreply_processing_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_poll_notifyreply_processing_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } goto cleanup; } if (ret == 2) { poll_seen = 1; if (notify_fd >= 0 && poll_kh) { for (uint32_t j = 0; j < loops * 2; j++) (void)syz_fuse_send_poll_kh( notify_fd, poll_kh); } break; } syz_fuse_sleep_ms(1); } } if (syz_fuse_send_retrieve_msg(notify_fd, notify_magic, nodeid, retrieve_off, retrieve_size) == -1) { syz_fuse_kmsg2("cleanup_initial_retrieve_fail", retrieve_off, errno); if (mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) goto skip_initial_retrieve_failure; ret = -1; goto cleanup; } skip_initial_retrieve_failure: if (mode & SYZ_FUSE_CR_SECOND_RETRIEVE) (void)syz_fuse_send_retrieve_msg(notify_fd, notify_magic + 1, alt_nodeid, store_off, store_size ? store_size : retrieve_size); if ((mode & SYZ_FUSE_CR_CLOSE_CLONE_AFTER_RETRIEVE) && clone_fd >= 0) { (void)close(clone_fd); syz_fuse_kmsg2("cleanup_clone_close_after_retrieve", clone_fd, (notify_fd == clone_fd) ? 1 : 0); if (notify_fd == clone_fd) notify_fd = -1; if (read_fd == clone_fd) read_fd = -1; clone_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_VALID_NOTIFY) && !(notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_RACE_NOTIFY) && notify_fd >= 0) { syz_fuse_send_edge_valid_notify_set( notify_fd, nodeid, alt_nodeid, parent, child, name, retrieve_off, retrieve_size, notify_magic); } memset(&thr_arg, 0, sizeof(thr_arg)); thr_arg.notify_fd = notify_fd; thr_arg.read_fd = read_fd; thr_arg.mountpoint = mountpoint; thr_arg.name = name; thr_arg.parent = parent; thr_arg.nodeid = nodeid; thr_arg.child = child; thr_arg.alt_nodeid = alt_nodeid; thr_arg.notify_unique = notify_magic; thr_arg.off = retrieve_off; thr_arg.size = retrieve_size; thr_arg.mode = mode; thr_arg.wait_ms = wait_ms; thr_arg.loops = loops; if (pthread_create(&thr, NULL, syz_fuse_cleanup_race_thread, &thr_arg) == 0) have_thread = 1; if ((notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_VALID_NOTIFY) && (notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_RACE_NOTIFY) && notify_fd >= 0 && pthread_create(&edge_thr, NULL, syz_fuse_edge_valid_race_thread, &thr_arg) == 0) have_edge_thread = 1; if ((notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_SPLICE_READ) && !(notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) && read_fd >= 0) { uint32_t pipe_mode = SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN; int splice_ret = syz_fuse_splice_retrieve_request( read_fd, notifyreply_pipe, buf_len, read_len, pipe_mode, wait_ms); syz_fuse_kmsg2("cleanup_initial_notifyreply_splice", splice_ret, notifyreply_pipe[0]); if (splice_ret > 0) { notifyreply_splice_done = 1; ret = 0; } else { syz_fuse_close_pipe(notifyreply_pipe); ret = -1; } } else if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); else ret = -1; if (notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_PRESSURE_NOTIFY) syz_fuse_edge_memory_pressure("cleanup_edge_pressure", loops); if (notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_ABORT_NOTIFY) syz_fuse_abort_all_connections_now("cleanup_edge_abort"); if (have_thread) (void)pthread_join(thr, NULL); have_thread = 0; if (have_edge_thread) (void)pthread_join(edge_thr, NULL); have_edge_thread = 0; if (file_pid > 0) { syz_fuse_wait_or_kill(file_pid); file_pid = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_POLL) && use_stateful && notify_fd >= 0 && read_fd >= 0 && mountpoint) { char poll_path[512]; int poll_file_fd; syz_fuse_join_path(mountpoint, "file", poll_path, sizeof(poll_path)); poll_file_fd = open(poll_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (poll_file_fd >= 0) { pid_t release_poll_pid = fork(); uint64_t release_poll_kh = 0; uint32_t poll_limit = loops * 4 + 32; int poll_wait = wait_ms > 10 ? 10 : wait_ms; syz_fuse_kmsg2("cleanup_early_postrelease_start", mode, loops); if (poll_limit > 128) poll_limit = 128; if (release_poll_pid == 0) { struct pollfd pfd; pfd.fd = poll_file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; for (uint32_t i = 0; i < loops + 8; i++) { (void)poll(&pfd, 1, poll_wait); syz_fuse_sleep_ms(1); } _exit(0); } for (uint32_t i = 0; i < poll_limit; i++) { int poll_ret; poll_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, poll_wait, &release_poll_kh, "syz_fuse_cleanup/early-post-release-poll-capture"); if (poll_ret < 0) break; if (poll_ret == 2 && release_poll_kh) break; syz_fuse_sleep_ms(1); } if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_RESEND) { syz_fuse_kmsg2( "cleanup_early_postrelease_resend_start", poll_file_fd, release_poll_kh); (void)syz_fuse_clone_resend_notify( read_fd, notify_fd, read_fd, poll_file_fd, (volatile long)buf, buf_len, (volatile long)req_out, 0, poll_wait); syz_fuse_kmsg2( "cleanup_early_postrelease_resend_done", poll_file_fd, release_poll_kh); } (void)close(poll_file_fd); if (release_poll_kh) { uint32_t poll_loops = loops * 4 + 16; if (poll_loops > 192) poll_loops = 192; if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_BEFORE_WAIT) { uint32_t before_loops = loops * 2 + 8; if (before_loops > poll_loops) before_loops = poll_loops; for (uint32_t i = 0; i < before_loops; i++) { (void)syz_fuse_send_poll_kh( notify_fd, release_poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (release_poll_pid > 0) syz_fuse_wait_or_kill(release_poll_pid); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_DELAY) { int delay_ms = wait_ms; if (delay_ms > 128) delay_ms = 128; syz_fuse_sleep_ms(delay_ms); } for (uint32_t i = 0; i < poll_loops; i++) { (void)syz_fuse_send_poll_kh( notify_fd, release_poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } poll_kh = release_poll_kh; poll_seen = 1; syz_fuse_kmsg2("cleanup_early_postrelease_done", release_poll_kh, poll_loops); } else if (release_poll_pid > 0) { syz_fuse_wait_or_kill(release_poll_pid); } } } if ((notify_magic & SYZ_FUSE_CR_MAGIC_CLOSE_CLONE_BEFORE_LATE) && clone_fd >= 0) { (void)close(clone_fd); syz_fuse_kmsg2("cleanup_clone_close_before_late", clone_fd, (notify_fd == clone_fd) ? 1 : 0); if (notify_fd == clone_fd) notify_fd = -1; if (read_fd == clone_fd) read_fd = -1; clone_fd = -1; } if ((mode & SYZ_FUSE_CR_STORE_RETRIEVE_AFTER_FAULT) && notify_fd >= 0) { (void)SYZ_FUSE_CLEANUP_SEND_STORE(notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg(notify_fd, notify_magic + 2, nodeid, retrieve_off, retrieve_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) && mountpoint) { uint32_t post_fault_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; size_t post_map_len = store_size > 65536 ? store_size : 65536; if (mode & SYZ_FUSE_CR_POLL_STORM) post_fault_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; syz_fuse_kmsg2("cleanup_early_poststore_fault_start", mode, loops); poststore_fault_pid = fork(); if (poststore_fault_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 64, post_fault_mode, post_map_len); _exit(0); } if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_ASYNC) && read_fd >= 0) { for (uint32_t i = 0; i < loops / 2 + 4; i++) { (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); if ((i & 3) == 3) syz_fuse_sleep_ms(1); } } if (poststore_fault_pid > 0 && !(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_ASYNC)) { syz_fuse_wait_or_kill(poststore_fault_pid); poststore_fault_pid = -1; } syz_fuse_kmsg2("cleanup_early_poststore_fault_done", mode, read_fd); } if ((mode & SYZ_FUSE_CR_CLOSE_CLONE_AFTER_LATE_NOTIFY) && clone_fd >= 0) { if ((notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_WINDOW | SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO)) && notify_fd >= 0) { uint32_t devend_loops = loops * 2 + 16; if (devend_loops > 192) devend_loops = 192; if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) && devend_loops > 32) devend_loops = 32; else if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) && devend_loops > 64) devend_loops = 64; syz_fuse_kmsg2("cleanup_devend_early_preclone_start", clone_fd, poll_kh); if (poll_kh) { for (uint32_t i = 0; i < devend_loops; i++) { (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (!(notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO))) { (void)SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 7, nodeid, retrieve_off, retrieve_size); } if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PRECLONE_STORE) { syz_fuse_kmsg2( "cleanup_devend_early_preclone_store", store_off, store_size); (void)SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x1a, nodeid, retrieve_off, retrieve_size); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND) && read_fd >= 0 && notify_fd >= 0 && mountpoint) { int reply_fd = (orig_read_fd >= 0 && orig_read_fd != read_fd) ? orig_read_fd : read_fd; int resend_mode = (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_ONESHOT) ? 0x7 : 0x17; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_NOREPLY) resend_mode |= 0x20; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_DUP) resend_mode |= 0x40; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) resend_mode |= 0x80; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_UNREAD) resend_mode |= 0x100; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) resend_mode |= 0x8; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY) resend_mode |= 0x200; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_TARGET_RELEASE) resend_mode |= 0x400; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_PREDRAIN) resend_mode |= 0x800; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_ONLY) resend_mode |= 0x1000; syz_fuse_kmsg2( "cleanup_devend_processing_resend_start", read_fd, reply_fd); (void)syz_fuse_processing_resend_path( read_fd, notify_fd, reply_fd, mountpoint, buf, buf_len, req_out, resend_mode, wait_ms); syz_fuse_kmsg2( "cleanup_devend_processing_resend_done", read_fd, reply_fd); } syz_fuse_kmsg2("cleanup_devend_early_preclone_done", devend_loops, read_fd); } (void)close(clone_fd); syz_fuse_kmsg2("cleanup_clone_close_after_late", clone_fd, (notify_fd == clone_fd) ? 1 : 0); if (notify_fd == clone_fd) notify_fd = -1; if (read_fd == clone_fd) { if ((notify_magic & SYZ_FUSE_CR_MAGIC_RESTORE_READ_AFTER_CLONE) && orig_read_fd >= 0 && orig_read_fd != clone_fd) { read_fd = orig_read_fd; syz_fuse_kmsg2( "cleanup_restore_read_after_clone", read_fd, clone_fd); } else { read_fd = -1; } } clone_fd = -1; if ((notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_WINDOW | SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO)) && notify_fd >= 0) { uint32_t devend_loops = loops * 2 + 16; if (devend_loops > 192) devend_loops = 192; if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) && devend_loops > 32) devend_loops = 32; else if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) && devend_loops > 64) devend_loops = 64; syz_fuse_kmsg2("cleanup_devend_early_postclone_start", notify_fd, poll_kh); if ((notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_PRESTORE_FAULT) && read_fd >= 0) { syz_fuse_kmsg2( "cleanup_devend_early_postclone_prestore_fault_start", read_fd, 1); (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); syz_fuse_kmsg2( "cleanup_devend_early_postclone_prestore_fault_done", read_fd, 1); } if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) syz_fuse_edge_memory_pressure( "cleanup_devend_early_postclone_micro_pressure", loops); if ((notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_PRESTORE_FAULT_AFTER_PRESSURE) && read_fd >= 0) { syz_fuse_kmsg2( "cleanup_devend_early_postclone_postpressure_fault_start", read_fd, 1); (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); syz_fuse_kmsg2( "cleanup_devend_early_postclone_postpressure_fault_done", read_fd, 1); } if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_POSTCLONE_STORE) { uint64_t retrieve_unique = (notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_RETRIEVE_BASE_UNIQUE) ? notify_magic : notify_magic + 0x18; syz_fuse_kmsg2( "cleanup_devend_early_postclone_store", store_off, store_size); (void)syz_fuse_send_store_msg( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, retrieve_unique, nodeid, retrieve_off, retrieve_size); if (notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_RETRIEVE_DUP_UNIQUE) { syz_fuse_kmsg2( "cleanup_devend_early_postclone_retrieve_dup_unique", retrieve_unique, retrieve_size); (void)syz_fuse_send_retrieve_msg( notify_fd, retrieve_unique, nodeid, retrieve_off, retrieve_size); } } if ((notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_READ) && read_fd >= 0) { uint32_t fault_loops = loops / 2 + 6; if (fault_loops > 32) fault_loops = 32; if (notify_magic & (SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_ONCE | SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_SANDWICH)) fault_loops = 1; syz_fuse_kmsg2( "cleanup_devend_early_postclone_fault_read_start", read_fd, fault_loops); for (uint32_t i = 0; i < fault_loops; i++) { (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); if ((i & 3) == 3) syz_fuse_sleep_ms(1); } syz_fuse_kmsg2( "cleanup_devend_early_postclone_fault_read_done", read_fd, fault_loops); if (notify_magic & SYZ_FUSE_CR_MAGIC_POSTCLONE_FAULT_SANDWICH) { syz_fuse_edge_memory_pressure( "cleanup_devend_early_postclone_fault_sandwich_pressure", loops); syz_fuse_kmsg2( "cleanup_devend_early_postclone_fault_sandwich_start", read_fd, 1); (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); syz_fuse_kmsg2( "cleanup_devend_early_postclone_fault_sandwich_done", read_fd, 1); } } if (poll_kh) { for (uint32_t i = 0; i < devend_loops; i++) { (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (!(notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO))) { (void)syz_fuse_send_store_msg( notify_fd, nodeid, retrieve_off, retrieve_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 8, nodeid, store_off, store_size ? store_size : retrieve_size); } if (!(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY)) syz_fuse_edge_memory_pressure( "cleanup_devend_early_postclone_pressure", loops); syz_fuse_kmsg2("cleanup_devend_early_postclone_done", notify_fd, read_fd); } } } if ((notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_POLL) && notify_fd >= 0) { if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2("cleanup_early_postrelease_poststore_abort", poll_kh, loops); syz_fuse_abort_all_connections_now( "cleanup_early_postrelease_poststore_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_early_postrelease_poststore_pressure", loops); if ((notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTABORT_FAULT) && mountpoint) { uint32_t post_fault_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; size_t post_map_len = store_size > 65536 ? store_size : 65536; if (mode & SYZ_FUSE_CR_POLL_STORM) post_fault_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; syz_fuse_kmsg2("cleanup_early_postabort_fault_start", mode, read_fd); postabort_fault_pid = fork(); if (postabort_fault_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 64, post_fault_mode, post_map_len); _exit(0); } if (read_fd >= 0) { for (uint32_t i = 0; i < loops / 2 + 4; i++) { (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); if ((i & 3) == 3) syz_fuse_sleep_ms(1); } } if (postabort_fault_pid > 0) { syz_fuse_wait_or_kill(postabort_fault_pid); postabort_fault_pid = -1; } syz_fuse_kmsg2("cleanup_early_postabort_fault_done", mode, read_fd); } } if (notify_fd >= 0) { if (mode & SYZ_FUSE_CR_DELETE_AFTER_READ) (void)syz_fuse_send_delete(notify_fd, parent, child, name); if (mode & SYZ_FUSE_CR_PRUNE_AFTER_READ) { if (mode & SYZ_FUSE_CR_MULTI_PRUNE) (void)syz_fuse_send_prune_vec( notify_fd, nodeid, alt_nodeid, parent, child); else (void)syz_fuse_send_prune_one(notify_fd, nodeid); } if (mode & SYZ_FUSE_CR_INVAL_AFTER_READ) (void)syz_fuse_send_inval_inode( notify_fd, nodeid, retrieve_off, retrieve_size); if ((mode & SYZ_FUSE_CR_INVAL_AFTER_READ) && (mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { (void)syz_fuse_send_inval_inode( notify_fd, nodeid, retrieve_off, 0x7ffffffffffff000ULL); (void)syz_fuse_send_inval_inode( notify_fd, nodeid, retrieve_off, 0xffffffffffffffffULL); (void)syz_fuse_send_inval_inode( notify_fd, nodeid, 0, 0xffffffffffffffffULL); (void)syz_fuse_send_inval_inode( notify_fd, nodeid, 0, 0x7fffffffffffffffULL); } if (mode & SYZ_FUSE_CR_INC_EPOCH_AFTER_READ) (void)syz_fuse_send_inc_epoch(notify_fd); if (mode & SYZ_FUSE_CR_RESEND_STORM) (void)syz_fuse_send_resend(notify_fd); if (notify_magic & SYZ_FUSE_CR_MAGIC_EMPTY_EXTRA) { (void)syz_fuse_send_empty_extra( notify_fd, FUSE_NOTIFY_INC_EPOCH_CODE, 0x1000); (void)syz_fuse_send_empty_extra( notify_fd, FUSE_NOTIFY_RESEND_CODE, 0x1000); (void)syz_fuse_send_empty_extra( notify_fd, FUSE_NOTIFY_INC_EPOCH_CODE, 0x100000); } if (mode & SYZ_FUSE_CR_POLL_STORM) { if (poll_seen && poll_kh) { for (uint32_t i = 0; i < loops * 2; i++) (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); } else { for (uint64_t kh = 1; kh <= 8; kh++) (void)syz_fuse_send_poll_kh(notify_fd, kh); } } } if ((notify_magic & SYZ_FUSE_CR_MAGIC_MALFORMED_NOTIFY) && !(mode & SYZ_FUSE_CR_LATE_POSTCOPY_TEARDOWN) && notify_fd >= 0) { syz_fuse_send_malformed_notify_set(notify_fd, nodeid, parent, child, name, retrieve_off, retrieve_size); } if ((mode & SYZ_FUSE_CR_LATE_POSTCOPY_TEARDOWN) && notify_fd >= 0) { pid_t late_pid = -1; pid_t burst_fault_pid = -1; pid_t high_fault_pid = -1; pid_t pre_fault_store_refresh_pid = -1; uint32_t late_fault_mode = 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; size_t late_map_len = store_size > 65536 ? store_size : 65536; if (mode & SYZ_FUSE_CR_POLL_STORM) late_fault_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; syz_fuse_kmsg2("cleanup_late_postcopy_start", mode, loops); if (store_size >= 1048576 && retrieve_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) && notify_fd >= 0 && read_fd == notify_fd) { int oldfd = read_fd; int forced_clone = open("/dev/fuse", O_RDWR | O_CLOEXEC); if (forced_clone >= 0 && ioctl(forced_clone, FUSE_DEV_IOC_CLONE, &oldfd) == 0) { read_fd = forced_clone; clone_fd = forced_clone; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_force_clone_read", read_fd, notify_fd); } else { syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_force_clone_fail", forced_clone, errno); if (forced_clone >= 0) (void)close(forced_clone); } } late_pid = fork(); if (late_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 128, late_fault_mode, late_map_len); _exit(0); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) && high_pinned_fd >= 0 && read_fd >= 0 && store_size >= 1048576 && retrieve_size >= 1048576 && read_len >= FUSE_MIN_READ_BUFFER && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { pre_fault_store_refresh_pid = syz_fuse_faultread_store_refresh_child( notify_fd, mountpoint, high_pinned_fd, read_fd, buf_len, req_out, &st, wait_ms, nodeid, store_off, retrieve_off, store_size, retrieve_size, notify_magic, loops > 4 ? loops / 2 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_pre_store_refresh_child", pre_fault_store_refresh_pid, high_pinned_fd); syz_fuse_sleep_ms(80); } (void)SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, store_off, store_size); (void)SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, retrieve_off, retrieve_size); (void)syz_fuse_send_retrieve_msg(notify_fd, notify_magic + 3, nodeid, retrieve_off, retrieve_size); if (store_size >= 1048576 && retrieve_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { uint32_t burst = loops / 2; if (burst < 6) burst = 6; if (burst > 16) burst = 16; if ((notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) && high_pinned_fd >= 0) { if (burst > 16) burst = 16; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_retrieve_burst_throttled", burst, high_pinned_fd); } else { syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_retrieve_burst", burst, retrieve_size); } for (uint32_t i = 0; i < burst; i++) (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x30 + i, nodeid, retrieve_off, retrieve_size); syz_fuse_send_resend_burst( notify_fd, burst / 2 + 1, "cleanup_late_postcopy_bigcopy_resend_after_burst", retrieve_size); if (mountpoint) { burst_fault_pid = fork(); if (burst_fault_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 160, late_fault_mode, late_map_len); _exit(0); } syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_burst_fault", burst_fault_pid, late_map_len); } } if ((mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) && mountpoint) { uint32_t burst = loops / 4; if (burst < 3) burst = 3; if (burst > 8) burst = 8; syz_fuse_kmsg2( "cleanup_late_postcopy_high_boundary_retrieve_burst", burst, retrieve_size); for (uint32_t i = 0; i < burst; i++) (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x70 + i, nodeid, retrieve_off, retrieve_size); high_fault_pid = fork(); if (high_fault_pid == 0) { syz_fuse_notify_high_offset_fault_child( mountpoint, high_pinned_fd, high_pinned_writable, store_off, store_size, loops + 32); _exit(0); } syz_fuse_kmsg2( "cleanup_late_postcopy_high_boundary_fault", high_fault_pid, store_size); } if (read_fd >= 0 && store_size >= 1048576 && retrieve_size >= 1048576 && read_len >= FUSE_MIN_READ_BUFFER && (read_len < retrieve_size + FUSE_MIN_READ_BUFFER || ((notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) && read_len <= retrieve_size + 0x100000)) && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { int short_ret = -1; char* short_buf = buf; void* short_fault_map = MAP_FAILED; pid_t short_store_refresh_pid = -1; pid_t short_during_read_teardown_pid = -1; pid_t short_during_read_poll_pid = -1; pid_t short_neartarget_abort_pid = -1; size_t short_fault_map_len = 0; size_t short_fault_prefix = 64; uint32_t short_small_notifyreply_drains = 0; uint32_t short_regular_drains = 0; int short_fresh_retrieve_armed = 0; if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) { if ((read_len & 0xfffff) == 0x10000) short_fault_prefix = 65536; else if ((read_len & 0xfffff) == 0x20000) short_fault_prefix = 131072; else if ((read_len & 0xfffff) == 0x30000) short_fault_prefix = 0x20048; else if ((read_len & 0xfffff) == 0x40000) short_fault_prefix = 0x2004f; else if ((read_len & 0xfffff) == 0x50000) short_fault_prefix = 0x40000; else if ((read_len & 0xfffff) == 0x60000) short_fault_prefix = 0x80000; else if ((read_len & 0xfffff) == 0x70000) short_fault_prefix = 0xc0000; else if ((read_len & 0xfffff) == 0x80000) short_fault_prefix = read_len >= 0x1000000 ? 0xfffff0 : read_len >= 0x800000 ? 0x7ffff0 : read_len >= 0x400000 ? 0x3ffff0 : 0xffff0; else if ((read_len & 0xfff) == 0) short_fault_prefix = 4096; else if ((read_len & 0xfff) == 0x90) short_fault_prefix = 80; if (short_fault_prefix >= read_len) short_fault_prefix = read_len > 4096 ? read_len - 4096 : 64; short_fault_map_len = ((short_fault_prefix + 4095) & ~4095) + 4096; short_fault_map = mmap( NULL, short_fault_map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (short_fault_map != MAP_FAILED && mprotect((char*)short_fault_map + short_fault_map_len - 4096, 4096, PROT_NONE) == 0) { short_buf = (char*)short_fault_map + short_fault_map_len - 4096 - short_fault_prefix; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_fault_read_buf", short_fault_prefix, read_len); } else { if (short_fault_map != MAP_FAILED) munmap(short_fault_map, short_fault_map_len); short_fault_map = MAP_FAILED; short_fault_map_len = 0; short_buf = buf; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_fault_read_fail", errno, read_len); } } if (read_fd >= 0) { for (uint32_t pred = 0; pred < 4; pred++) { int pred_ret; if (syz_fuse_wait_readable(read_fd, wait_ms) != 0) break; pred_ret = read(read_fd, buf, buf_len); if (pred_ret < (int)sizeof(struct fuse_in_header)) break; const struct fuse_in_header* pred_hdr = (const struct fuse_in_header*)buf; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_predrain_hdr", pred_hdr->opcode, pred_hdr->len); if (pred_hdr->len <= (uint32_t)pred_ret && pred_hdr->opcode != FUSE_NOTIFY_REPLY) { struct fuse_out_header* pred_out = NULL; int pred_pick = syz_fuse_pick_response( req_out, pred_hdr->opcode, &pred_out, "cleanup_faultread_predrain"); int pred_resp = pred_pick; if (pred_pick == 0 && pred_out) { syz_fuse_force_stateful_reply_success( pred_out, pred_hdr->opcode); pred_resp = fuse_send_response( read_fd, pred_hdr, pred_out); } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_predrain_reply", pred_hdr->opcode, pred_resp); } } short_fresh_retrieve_armed = 1; } if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ && !short_fresh_retrieve_armed) short_store_refresh_pid = pre_fault_store_refresh_pid > 0 ? pre_fault_store_refresh_pid : syz_fuse_faultread_store_refresh_child( notify_fd, mountpoint, high_pinned_fd, read_fd, buf_len, req_out, &st, wait_ms, nodeid, store_off, retrieve_off, store_size, retrieve_size, notify_magic, loops); if ((notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) && !short_fresh_retrieve_armed) { uint32_t short_during_delay_ms = wait_ms & 15; if (short_during_delay_ms == 0) short_during_delay_ms = 1; if ((mode & SYZ_FUSE_CR_POLL_STORM) && notify_fd >= 0 && poll_kh) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_poll_context", poll_pid, poll_kh); short_during_read_poll_pid = fork(); if (short_during_read_poll_pid == 0) { uint32_t poll_delay_ms = short_during_delay_ms > 1 ? short_during_delay_ms - 1 : 1; uint32_t poll_loops = loops * 4 + 32; if (poll_loops > 192) poll_loops = 192; syz_fuse_sleep_ms( poll_delay_ms); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_poll_start", poll_kh, poll_delay_ms); for (uint32_t i = 0; i < poll_loops; i++) { (void)syz_fuse_send_poll_kh( notify_fd, poll_kh); if (i == 7 && poll_pid > 0) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_poll_kill_waiter", poll_pid, poll_kh); (void)kill(poll_pid, SIGKILL); } if ((i & 3) == 3) syz_fuse_sleep_ms(1); } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_poll_done", poll_kh, poll_loops); _exit(0); } } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_arm", short_fresh_retrieve_armed, short_during_delay_ms); short_during_read_teardown_pid = fork(); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_fork", short_during_read_teardown_pid, errno); if (short_during_read_teardown_pid == 0) { syz_fuse_sleep_ms( short_during_delay_ms); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_teardown_start", loops, short_during_delay_ms); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_during_read_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_during_read_pressure", loops); _exit(0); } } if (short_fresh_retrieve_armed && notify_fd >= 0) { if (retrieve_size <= 1048576) { uint32_t fresh_delay_ms = wait_ms & 3; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_arm", fresh_delay_ms, loops); short_during_read_teardown_pid = fork(); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_fork", short_during_read_teardown_pid, errno); if (short_during_read_teardown_pid == 0) { uint32_t fresh_abort_loops = loops; if (fresh_delay_ms) syz_fuse_sleep_ms(fresh_delay_ms); if (fresh_abort_loops < 8) fresh_abort_loops = 8; if (fresh_abort_loops > 64) fresh_abort_loops = 64; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_abort", fresh_delay_ms, fresh_abort_loops); for (uint32_t ai = 0; ai < fresh_abort_loops; ai++) syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_fresh_during_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_fresh_during_pressure", loops); _exit(0); } } uint64_t fresh_cover = 0; uint32_t fresh_chunk = store_size ? store_size : 4096; uint32_t fresh_store_ok = 0; uint32_t fresh_store_fail = 0; if (fresh_chunk > 1048576) fresh_chunk = 1048576; if (store_splice_write) { uint32_t fresh_max_store = 1048560 - sizeof(struct fuse_notify_store_head); if (store_vmsplice_gift && store_vmsplice_split && fresh_max_store > 1044480) fresh_max_store = 1044480; if (fresh_chunk > fresh_max_store) fresh_chunk = fresh_max_store; } while (fresh_cover < retrieve_size) { uint32_t this_chunk = fresh_chunk; int fresh_store_ret; if ((uint64_t)this_chunk > retrieve_size - fresh_cover) this_chunk = (uint32_t)(retrieve_size - fresh_cover); if ((uint32_t)notify_magic & 8U) fresh_store_ret = syz_fuse_send_store_msg( notify_fd, nodeid, retrieve_off + fresh_cover, this_chunk); else fresh_store_ret = SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, retrieve_off + fresh_cover, this_chunk); if (fresh_store_ret == 0) fresh_store_ok++; else fresh_store_fail++; fresh_cover += this_chunk; if (!this_chunk) break; } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_store_cover", fresh_cover, ((long)fresh_store_ok << 16) | fresh_store_fail); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x500, nodeid, retrieve_off, retrieve_size); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_retrieve", retrieve_size, read_len); if (((uint32_t)notify_magic & 8U) && read_fd >= 0 && retrieve_size > 1048576) { int fresh_pipe[2] = {-1, -1}; uint32_t fresh_pipe_mode = SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN; int fresh_splice_ret; pid_t fresh_midabort_pid = -1; pid_t fresh_pipe_ops_pid = -1; int fresh_midabort_delay = 0; int fresh_postfileop_abort = 0; int fresh_abort_first = 0; int fresh_parent_pipe_ops_done = 0; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_splice_enter", retrieve_size, read_len); if ((raw_wait_ms & 1) || (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_MIDABORT)) { fresh_midabort_delay = (raw_wait_ms >> 8) & 0xff; if (fresh_midabort_delay == 0) fresh_midabort_delay = wait_ms > 128 ? wait_ms / 64 : 1; if (fresh_midabort_delay < 1) fresh_midabort_delay = 1; if (fresh_midabort_delay > 250) fresh_midabort_delay = 250; if ((raw_wait_ms & 1) && fresh_midabort_delay >= 240) { fresh_postfileop_abort = 1; fresh_abort_first = fresh_midabort_delay >= 248 || ((raw_wait_ms & 0xff00) == 0xfa00); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_postfileop_abort_arm", fresh_midabort_delay, loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_postfileop_abort_mode", raw_wait_ms, ((uint64_t)fresh_midabort_delay << 32) | fresh_abort_first); } else { fresh_midabort_pid = fork(); if (fresh_midabort_pid == 0) { syz_fuse_sleep_ms(fresh_midabort_delay); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_fresh_midabort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_fresh_midpressure", loops > 1 ? loops / 2 : loops); _exit(0); } } } fresh_splice_ret = syz_fuse_splice_retrieve_request( read_fd, fresh_pipe, read_len, retrieve_size + FUSE_MIN_READ_BUFFER, fresh_pipe_mode, wait_ms); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_splice", fresh_splice_ret, fresh_pipe[0]); if (fresh_splice_ret > 0 && fresh_postfileop_abort) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_noreply_request_end", notify_magic + 0x500, fresh_pipe[0]); syz_fuse_postsplice_parent_fileops( read_fd, notify_fd, high_pinned_fd, buf_len, req_out, &st, wait_ms, loops, retrieve_size, fresh_abort_first, poll_kh, mode, mountpoint); if (fresh_pipe[0] >= 0) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_pipe_ops", fresh_pipe[0], fresh_pipe[1]); fresh_pipe_ops_pid = syz_fuse_fork_hold_pipe_buffers( fresh_pipe, fresh_pipe_mode, (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) ? 96 : 180 + (loops > 16 ? 16 : loops) * 2, "cleanup_late_postcopy_faultread_parent_postsplice_pipe_hold_child"); syz_fuse_sleep_ms(1); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_pipe_ops_child_deferred", fresh_pipe_ops_pid, fresh_pipe[0]); fresh_parent_pipe_ops_done = 1; } if ((mode & SYZ_FUSE_CR_POLL_STORM) && notify_fd >= 0 && poll_kh) { uint32_t poll_loops = loops * 4 + 16; pid_t post_abort_poll_pid = syz_fuse_fork_poll_kh_burst( notify_fd, poll_kh, poll_loops, "cleanup_late_postcopy_faultread_parent_postsplice_poll_after_abort", "cleanup_late_postcopy_faultread_parent_postsplice_poll_after_abort_done"); syz_fuse_sleep_ms(2); syz_fuse_kill_nowait_log( &post_abort_poll_pid, "cleanup_late_postcopy_faultread_parent_postsplice_poll_after_abort_kill_nowait"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) { if (clone_fd >= 0) { int closed_clone = clone_fd; if ((notify_magic & SYZ_FUSE_CR_MAGIC_LASTDEV_CLOSE_BEFORE_FINAL) && notify_fd >= 0 && notify_fd != clone_fd) { int closed_notify = notify_fd; int notify_flags = fcntl(notify_fd, F_GETFL, 0); int setown_ret = fcntl(notify_fd, F_SETOWN, getpid()); int async_ret = -1; if (notify_flags >= 0) async_ret = fcntl( notify_fd, F_SETFL, notify_flags | O_ASYNC | O_NONBLOCK); syz_fuse_kmsg2( "cleanup_late_postcopy_finalfirst_fasync_arm", setown_ret, async_ret); (void)close(notify_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_finalfirst_notify_close", closed_notify, clone_fd); if (read_fd == closed_notify) read_fd = -1; notify_fd = -1; } (void)close(clone_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_finalfirst_clone_close", closed_clone, fresh_pipe[0]); if (read_fd == closed_clone) read_fd = -1; if (notify_fd == closed_clone) notify_fd = -1; clone_fd = -1; } syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_finalfirst_abort"); if (fresh_pipe_ops_pid > 0) syz_fuse_kmsg2( "cleanup_late_postcopy_finalfirst_pipe_hold_alive", fresh_pipe_ops_pid, fresh_pipe[0]); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO)) syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_finalfirst_pressure", loops > 4 ? 1 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_finalfirst_after", fresh_pipe_ops_pid, loops); } } pid_t fresh_fileop_pid = -1; if (fresh_splice_ret > 0 && mountpoint && notify_fd >= 0) { fresh_fileop_pid = syz_fuse_faultread_store_refresh_child( notify_fd, mountpoint, high_pinned_fd, read_fd, buf_len, req_out, &st, wait_ms, nodeid, store_off, retrieve_off, store_size, retrieve_size, notify_magic + 0x900, loops > 4 ? loops / 2 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_fileop_child", fresh_fileop_pid, retrieve_size); if (fresh_postfileop_abort) { int post_ms = fresh_midabort_delay; if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) { post_ms = wait_ms & 15; if (post_ms < 1) post_ms = 1; } else { if (post_ms < 40) post_ms = 40; post_ms += (int)(loops * 4); if (post_ms > 500) post_ms = 500; } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_postfileop_abort_wait", post_ms, fresh_fileop_pid); syz_fuse_sleep_ms(post_ms); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_fresh_postfileop_abort"); if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_postfileop_pressure_skipped_finalfirst", post_ms, loops); else { syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_fresh_postfileop_pressure", loops > 4 ? 4 : loops); } syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_postfileop_after_pressure", post_ms, loops); } else { syz_fuse_sleep_ms(wait_ms > 20 ? 20 : wait_ms); } } if (fresh_midabort_pid > 0) { syz_fuse_wait_or_kill(fresh_midabort_pid); fresh_midabort_pid = -1; } if (fresh_splice_ret > 0) { if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_TRUNC_REPLY) { (void)syz_fuse_send_notifyreply_reply( read_fd, notify_magic + 0x500, 0, 1, "cleanup_fresh_splice_trunc_reply"); if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_DUP_REPLY) (void)syz_fuse_send_notifyreply_reply( read_fd, notify_magic + 0x500, 0, 1, "cleanup_fresh_splice_dup_trunc_reply"); } else if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_ERROR_REPLY) { (void)syz_fuse_send_notifyreply_reply( read_fd, notify_magic + 0x500, -EIO, 0, "cleanup_fresh_splice_error_reply"); if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_DUP_REPLY) (void)syz_fuse_send_notifyreply_reply( read_fd, notify_magic + 0x500, -EIO, 0, "cleanup_fresh_splice_dup_error_reply"); } if (clone_fd >= 0) { int closed_clone = clone_fd; if ((notify_magic & SYZ_FUSE_CR_MAGIC_LASTDEV_CLOSE_BEFORE_FINAL) && notify_fd >= 0 && notify_fd != clone_fd) { int closed_notify = notify_fd; (void)close(notify_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_notify_close_before_final_clone_close", closed_notify, clone_fd); if (read_fd == closed_notify) read_fd = -1; notify_fd = -1; } (void)close(clone_fd); syz_fuse_kmsg2( "cleanup_late_postcopy_clone_close_before_final_abort", closed_clone, fresh_pipe[0]); if (read_fd == closed_clone) read_fd = -1; if (notify_fd == closed_clone) notify_fd = -1; clone_fd = -1; } syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_fresh_splice_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_fresh_splice_pressure", loops > 4 ? 4 : loops); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_splice_after_pressure", fresh_splice_ret, loops); if (fresh_pipe_ops_pid > 0) syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_pipe_hold_child_alive_after_final_abort", fresh_pipe_ops_pid, fresh_pipe[0]); if (!fresh_parent_pipe_ops_done) syz_fuse_post_teardown_pipe_ops( fresh_pipe, fresh_pipe_mode); } if (fresh_fileop_pid > 0) syz_fuse_wait_or_kill(fresh_fileop_pid); syz_fuse_close_pipe(fresh_pipe); if (fresh_pipe_ops_pid > 0) { syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_pipe_hold_child_wait_after_parent_close", fresh_pipe_ops_pid, fresh_splice_ret); syz_fuse_wait_or_kill(fresh_pipe_ops_pid); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_parent_postsplice_pipe_hold_child_wait_done", fresh_pipe_ops_pid, fresh_splice_ret); fresh_pipe_ops_pid = -1; } } if (retrieve_size > 1048576) { uint32_t fresh_delay_ms = wait_ms; if (fresh_delay_ms == 0) fresh_delay_ms = 4; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_arm", fresh_delay_ms, loops); short_during_read_teardown_pid = fork(); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_fork", short_during_read_teardown_pid, errno); if (short_during_read_teardown_pid == 0) { uint32_t fresh_abort_loops = loops; if (fresh_delay_ms) syz_fuse_sleep_ms(fresh_delay_ms); if (fresh_abort_loops < 8) fresh_abort_loops = 8; if (fresh_abort_loops > 64) fresh_abort_loops = 64; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_fresh_during_abort", fresh_delay_ms, fresh_abort_loops); for (uint32_t ai = 0; ai < fresh_abort_loops; ai++) syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_fresh_during_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_fresh_during_pressure", loops); _exit(0); } } } for (uint32_t short_try = 0; short_try < 32; short_try++) { uint32_t short_neartarget_regular_need = 2; if (short_neartarget_abort_pid < 0 && !((uint32_t)notify_magic & 8U) && short_fresh_retrieve_armed && retrieve_size > 1048576 && short_small_notifyreply_drains >= 12 && short_regular_drains >= short_neartarget_regular_need) { uint32_t near_delay_ms = (wait_ms >> 6) & 15; syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_neartarget_abort_arm", short_small_notifyreply_drains, near_delay_ms); short_neartarget_abort_pid = fork(); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_neartarget_abort_fork", short_neartarget_abort_pid, errno); if (short_neartarget_abort_pid == 0) { if (near_delay_ms) syz_fuse_sleep_ms(near_delay_ms); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_neartarget_abort", short_regular_drains, loops); for (uint32_t ai = 0; ai < 16; ai++) syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_faultread_neartarget_abort"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_faultread_neartarget_pressure", loops); _exit(0); } } if (syz_fuse_wait_readable(read_fd, wait_ms) == 0) { short_ret = read(read_fd, short_buf, read_len); if (short_ret < 0) { short_ret = -errno; break; } } else { short_ret = -1000 - errno; break; } if (short_ret < (int)sizeof(struct fuse_in_header)) break; const struct fuse_in_header* drain_hdr = (const struct fuse_in_header*)short_buf; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_try_hdr", drain_hdr->opcode, drain_hdr->len); syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_try_unique", drain_hdr->unique, drain_hdr->nodeid); if (drain_hdr->opcode == FUSE_NOTIFY_REPLY) { st.captured_notifyreply_unique = drain_hdr->unique; st.captured_notifyreply_nodeid = drain_hdr->nodeid; st.captured_notifyreply_len = drain_hdr->len; syz_fuse_kmsg2( "cleanup_bigcopy_short_read_capture_notifyreply", drain_hdr->unique, drain_hdr->len); syz_fuse_reply_captured_notifyreply( read_fd, &st, notify_magic); if (drain_hdr->len < read_len && short_try + 1 < 32) { short_small_notifyreply_drains++; syz_fuse_kmsg2( "cleanup_bigcopy_short_read_drain_small_notifyreply", drain_hdr->len, read_len); short_ret = -3000 - (int)drain_hdr->opcode; continue; } break; } if (drain_hdr->len <= (uint32_t)short_ret) { struct fuse_out_header* drain_out = NULL; int drain_pick; int drain_resp = -1; drain_pick = syz_fuse_pick_response( req_out, drain_hdr->opcode, &drain_out, "cleanup_bigcopy_short_read_drain"); if (drain_pick == 0 && drain_out) { syz_fuse_force_stateful_reply_success( drain_out, drain_hdr->opcode); drain_resp = fuse_send_response(read_fd, drain_hdr, drain_out); } syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_drain_reply", drain_hdr->opcode, drain_pick == 0 ? drain_resp : drain_pick); short_regular_drains++; short_ret = -2000 - (int)drain_hdr->opcode; continue; } syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_drain_truncated", drain_hdr->opcode, short_ret); break; } syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read", short_ret, read_len); if (short_ret >= (int)sizeof(struct fuse_in_header)) { const struct fuse_in_header* short_hdr = (const struct fuse_in_header*)short_buf; syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_hdr", short_hdr->opcode, short_hdr->len); syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_unique", short_hdr->unique, short_hdr->nodeid); } if (short_ret >= 0) { syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); } if (short_ret < 0) { syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_error_teardown", short_ret, read_fd); if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) syz_fuse_faultread_store_refresh_nowait( notify_fd, nodeid, store_off, retrieve_off, store_size, retrieve_size, notify_magic, loops, "cleanup_late_postcopy_faultread_store_refresh_error_nowait"); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_bigcopy_short_read_abort_prepressure"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_bigcopy_short_read_pressure", loops); if (loops > 40) syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_bigcopy_short_read_pressure2", loops); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops + 8, wait_ms, notify_magic, store_size); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops + 16, wait_ms, notify_magic, store_size); } else if (short_ret > 0 && (uint64_t)short_ret < read_len) { syz_fuse_kmsg2( "cleanup_late_postcopy_bigcopy_short_read_partial_teardown", short_ret, read_len); if (notify_magic & SYZ_FUSE_CR_MAGIC_NOTIFYREPLY_FAULT_READ) syz_fuse_faultread_store_refresh( notify_fd, read_fd, mountpoint, nodeid, store_off, retrieve_off, store_size, retrieve_size, notify_magic + 0x80, loops, wait_ms, mode, read_len, "cleanup_late_postcopy_faultread_store_refresh_partial"); syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_bigcopy_short_read_partial_pressure", loops + 4); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops + 6, wait_ms, notify_magic, store_size); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_bigcopy_short_read_partial_abort"); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops + 14, wait_ms, notify_magic, store_size); } if (short_store_refresh_pid > 0) { syz_fuse_wait_or_kill( short_store_refresh_pid); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_store_refresh_child_done", short_store_refresh_pid, short_ret); } if (short_during_read_teardown_pid > 0) { syz_fuse_wait_or_kill( short_during_read_teardown_pid); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_teardown_done", short_during_read_teardown_pid, short_ret); } if (short_during_read_poll_pid > 0) { syz_fuse_wait_or_kill( short_during_read_poll_pid); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_during_read_poll_child_done", short_during_read_poll_pid, short_ret); } if (short_neartarget_abort_pid > 0) { syz_fuse_wait_or_kill( short_neartarget_abort_pid); syz_fuse_kmsg2( "cleanup_late_postcopy_faultread_neartarget_abort_done", short_neartarget_abort_pid, short_ret); } if (short_fault_map != MAP_FAILED) munmap(short_fault_map, short_fault_map_len); } syz_fuse_kmsg2( "cleanup_late_notifyreply_splice_precheck", (uint32_t)notify_magic, read_fd); if (((uint32_t)notify_magic & 8U) && read_fd >= 0) { uint32_t pipe_mode = SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN; int splice_ret; syz_fuse_kmsg2( "cleanup_late_notifyreply_splice_enter", (uint32_t)notify_magic, read_fd); splice_ret = syz_fuse_splice_retrieve_request( read_fd, notifyreply_pipe, buf_len, late_map_len, pipe_mode, wait_ms); syz_fuse_kmsg2( "cleanup_late_notifyreply_splice", splice_ret, notifyreply_pipe[0]); if (splice_ret > 0) notifyreply_splice_done = 1; else syz_fuse_close_pipe(notifyreply_pipe); } (void)syz_fuse_send_inval_inode(notify_fd, nodeid, retrieve_off, retrieve_size); if (mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) { (void)syz_fuse_send_inval_inode( notify_fd, nodeid, retrieve_off, 0x7ffffffffffff000ULL); (void)syz_fuse_send_inval_inode( notify_fd, nodeid, 0, 0xffffffffffffffffULL); } (void)syz_fuse_send_delete(notify_fd, parent, child, name); if (mode & SYZ_FUSE_CR_MULTI_PRUNE) (void)syz_fuse_send_prune_vec(notify_fd, nodeid, alt_nodeid, parent, child); else (void)syz_fuse_send_prune_one(notify_fd, nodeid); (void)syz_fuse_send_inc_epoch(notify_fd); if ((mode & SYZ_FUSE_CR_POLL_STORM) && poll_kh) { for (uint32_t i = 0; i < loops * 2; i++) (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); } if (notify_magic & SYZ_FUSE_CR_MAGIC_EDGE_VALID_NOTIFY) syz_fuse_send_edge_valid_notify_set( notify_fd, nodeid, alt_nodeid, parent, child, name, retrieve_off, retrieve_size, notify_magic + 0x1000); if (notify_magic & SYZ_FUSE_CR_MAGIC_MALFORMED_NOTIFY) syz_fuse_send_malformed_notify_set( notify_fd, nodeid, parent, child, name, retrieve_off, retrieve_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_LATE_DRAIN) && read_fd >= 0) { uint32_t drain_loops = loops * 4 + 16; uint32_t drain_hits = 0; int bigcopy_midvalid_teardown_done = 0; if (drain_loops > 192) drain_loops = 192; for (uint32_t i = 0; i < drain_loops; i++) { int drain_ret; if (mode & SYZ_FUSE_CR_POLL_STORM) { drain_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, wait_ms, &poll_kh, "syz_fuse_cleanup/late-drain-poll"); } else { drain_ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_cleanup/late-drain"); } if (drain_ret < 0) { break; } if (drain_ret == 4 && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_SKIP_NOTIFY)) { syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_no_resend", read_fd, notify_fd); if (store_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) syz_fuse_send_resend_burst( notify_fd, 3, "cleanup_late_drain_notifyreply_resend_while_processing", drain_hits); if (store_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE) && read_fd >= 0) { uint64_t old_unique = st.captured_notifyreply_unique; uint64_t old_nodeid = st.captured_notifyreply_nodeid; for (uint32_t retry_i = 0; retry_i < 3; retry_i++) { int recapture_ret; recapture_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, wait_ms, &poll_kh, "syz_fuse_cleanup/late-drain-resend-recapture"); syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_resend_recapture", recapture_ret, st.captured_notifyreply_unique); if (recapture_ret == 4 && st.captured_notifyreply_unique && st.captured_notifyreply_unique != old_unique) { syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_resend_new_unique", old_unique, st.captured_notifyreply_unique); syz_fuse_reply_captured_notifyreply( read_fd, &st, notify_magic); break; } } st.captured_notifyreply_unique = old_unique; st.captured_notifyreply_nodeid = old_nodeid; } syz_fuse_reply_captured_notifyreply( read_fd, &st, notify_magic); if (!bigcopy_midvalid_teardown_done && store_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { bigcopy_midvalid_teardown_done = 1; syz_fuse_kmsg2( "cleanup_late_drain_bigcopy_midvalid_teardown", drain_hits, read_fd); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); syz_fuse_abort_all_connections_now( "cleanup_late_drain_bigcopy_midvalid_teardown"); syz_fuse_edge_memory_pressure( "cleanup_late_drain_bigcopy_midvalid_pressure", loops); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_PREDRAIN) && (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_close_read_early", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT) { syz_fuse_send_resend_burst( notify_fd, 4, "cleanup_late_drain_notifyreply_resend_prefault", drain_hits); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_abort", read_fd, notify_fd); syz_fuse_abort_all_connections_now( "cleanup_late_drain_notifyreply_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) { syz_fuse_edge_memory_pressure( "cleanup_late_drain_notifyreply_pressure", loops); } if (!(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POSTSTORE_FAULT)) syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_late_drain_notifyreply_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } drain_hits++; break; } if (drain_ret == 2 && notify_fd >= 0 && poll_kh) (void)syz_fuse_send_poll_kh( notify_fd, poll_kh); if (drain_ret > 0) drain_hits++; if (drain_hits > loops) break; } syz_fuse_kmsg2("cleanup_late_drain", drain_hits, drain_loops); if (drain_hits >= 4 && read_fd >= 0 && store_size >= 1048576 && retrieve_size >= 1048576 && !(mode & SYZ_FUSE_CR_HIGH_BOUNDARY_STORE)) { syz_fuse_kmsg2( "cleanup_late_drain_bigcopy_multi_teardown", drain_hits, read_fd); syz_fuse_send_resend_burst( notify_fd, 8, "cleanup_late_drain_bigcopy_multi_resend", drain_hits); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); syz_fuse_abort_all_connections_now( "cleanup_late_drain_bigcopy_multi_teardown"); syz_fuse_edge_memory_pressure( "cleanup_late_drain_bigcopy_multi_pressure", loops); syz_fuse_release_resend_fault_window( read_fd, mountpoint, read_len, mode, loops, wait_ms, notify_magic, store_size); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_NOTIFY) && notify_fd >= 0 && notify_fd != read_fd) { syz_fuse_kmsg2( "cleanup_late_drain_bigcopy_multi_close_notify", notify_fd, read_fd); (void)close(notify_fd); notify_fd = -1; } if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PROCESSING_RESEND_CLOSE_REPLY) && read_fd >= 0) { int closed_fd = read_fd; syz_fuse_kmsg2( "cleanup_late_drain_bigcopy_multi_close_read", read_fd, notify_fd); (void)close(read_fd); if (closed_fd == clone_fd) clone_fd = -1; read_fd = -1; } } } if (notify_magic & SYZ_FUSE_CR_MAGIC_LATE_POSTCOPY_ABORT) { syz_fuse_kmsg2("cleanup_late_postcopy_abort", mode, loops); syz_fuse_abort_all_connections_now( "cleanup_late_postcopy_abort"); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_LATE_POSTCOPY_PRESSURE) && !(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY)) syz_fuse_edge_memory_pressure( "cleanup_late_postcopy_pressure", loops); syz_fuse_sleep_ms(wait_ms > 5 ? wait_ms : 5); if (!(notify_magic & SYZ_FUSE_CR_MAGIC_LATE_KEEP_MOUNT) && mountpoint) (void)umount2(mountpoint, MNT_DETACH); if ((notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_WINDOW | SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO)) && notify_fd >= 0 && clone_fd >= 0) { uint32_t devend_loops = loops * 2 + 16; if (devend_loops > 192) devend_loops = 192; if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) && devend_loops > 32) devend_loops = 32; else if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) && devend_loops > 64) devend_loops = 64; syz_fuse_kmsg2("cleanup_devend_preclone_start", clone_fd, poll_kh); if (poll_kh) { for (uint32_t i = 0; i < devend_loops; i++) { (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (!(notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO))) { (void)SYZ_FUSE_CLEANUP_SEND_STORE( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 5, nodeid, retrieve_off, retrieve_size); } if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_PRECLONE_STORE) { syz_fuse_kmsg2("cleanup_devend_preclone_store", store_off, store_size); (void)syz_fuse_send_store_msg( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x1b, nodeid, retrieve_off, retrieve_size); } syz_fuse_kmsg2("cleanup_devend_preclone_done", devend_loops, read_fd); } if (clone_fd >= 0) { (void)close(clone_fd); if (notify_fd == clone_fd) notify_fd = -1; if (read_fd == clone_fd) read_fd = -1; clone_fd = -1; } if ((notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_WINDOW | SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO)) && !(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY) && notify_fd >= 0) { uint32_t devend_loops = loops * 2 + 16; if (devend_loops > 192) devend_loops = 192; if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) && devend_loops > 32) devend_loops = 32; else if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT) && devend_loops > 64) devend_loops = 64; syz_fuse_kmsg2("cleanup_devend_postclone_start", notify_fd, poll_kh); if (notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_MICRO) syz_fuse_edge_memory_pressure( "cleanup_devend_postclone_micro_pressure", loops); if ((notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_POSTCLONE_STORE) && !(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_STORE_EARLY_ONLY)) { syz_fuse_kmsg2( "cleanup_devend_postclone_store", store_off, store_size); (void)syz_fuse_send_store_msg( notify_fd, nodeid, store_off, store_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 0x19, nodeid, retrieve_off, retrieve_size); } if (poll_kh) { for (uint32_t i = 0; i < devend_loops; i++) { (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (!(notify_magic & (SYZ_FUSE_CR_MAGIC_DEVEND_LIGHT | SYZ_FUSE_CR_MAGIC_DEVEND_MICRO))) { (void)syz_fuse_send_store_msg( notify_fd, nodeid, retrieve_off, retrieve_size); (void)syz_fuse_send_retrieve_msg( notify_fd, notify_magic + 6, nodeid, store_off, store_size ? store_size : retrieve_size); } if (!(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY)) syz_fuse_edge_memory_pressure( "cleanup_devend_postclone_pressure", loops); if (read_fd >= 0) { for (uint32_t i = 0; i < 16; i++) { int drain_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, wait_ms, &poll_kh, "syz_fuse_cleanup/devend-postclone"); if (drain_ret < 0) break; if (drain_ret == 2 && poll_kh) (void)syz_fuse_send_poll_kh( notify_fd, poll_kh); } } syz_fuse_kmsg2("cleanup_devend_postclone_done", notify_fd, read_fd); } if (!(notify_magic & SYZ_FUSE_CR_MAGIC_LATE_KEEP_NOTIFY) && notify_fd >= 0) { int old_notify = notify_fd; (void)close(notify_fd); notify_fd = -1; if (read_fd == old_notify) read_fd = -1; } if (notify_magic & SYZ_FUSE_CR_MAGIC_LATE_POSTCLOSE_ABORT) { syz_fuse_kmsg2("cleanup_late_postclose_abort", mode, loops); syz_fuse_abort_all_connections_now( "cleanup_late_postclose_abort"); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_LATE_POSTCLOSE_PRESSURE) && !(notify_magic & SYZ_FUSE_CR_MAGIC_DEVEND_EARLY_ONLY)) syz_fuse_edge_memory_pressure( "cleanup_late_postclose_pressure", loops); if (burst_fault_pid > 0) syz_fuse_wait_or_kill(burst_fault_pid); if (high_fault_pid > 0) syz_fuse_wait_or_kill(high_fault_pid); if (late_pid > 0) syz_fuse_wait_or_kill(late_pid); syz_fuse_kmsg2("cleanup_late_postcopy_done", mode, late_pid); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL) && !(notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_POLL) && use_stateful && notify_fd >= 0 && read_fd >= 0 && mountpoint) { char poll_path[512]; int poll_file_fd; syz_fuse_join_path(mountpoint, "file", poll_path, sizeof(poll_path)); poll_file_fd = open(poll_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (poll_file_fd >= 0) { pid_t release_poll_pid = fork(); uint64_t release_poll_kh = 0; uint32_t poll_limit = loops * 4 + 32; int poll_wait = wait_ms > 10 ? 10 : wait_ms; if (poll_limit > 128) poll_limit = 128; if (release_poll_pid == 0) { struct pollfd pfd; pfd.fd = poll_file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; for (uint32_t i = 0; i < loops + 8; i++) { (void)poll(&pfd, 1, poll_wait); syz_fuse_sleep_ms(1); } _exit(0); } for (uint32_t i = 0; i < poll_limit; i++) { int poll_ret; poll_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, poll_wait, &release_poll_kh, "syz_fuse_cleanup/post-release-poll-capture"); if (poll_ret < 0) break; if (poll_ret == 2 && release_poll_kh) break; syz_fuse_sleep_ms(1); } if (notify_magic & SYZ_FUSE_CR_MAGIC_EARLY_POST_RELEASE_RESEND) { syz_fuse_kmsg2( "cleanup_postrelease_resend_start", poll_file_fd, release_poll_kh); (void)syz_fuse_clone_resend_notify( read_fd, notify_fd, read_fd, poll_file_fd, (volatile long)buf, buf_len, (volatile long)req_out, 0, poll_wait); syz_fuse_kmsg2( "cleanup_postrelease_resend_done", poll_file_fd, release_poll_kh); } (void)close(poll_file_fd); if (release_poll_kh) { uint32_t poll_loops = loops * 4 + 16; if (poll_loops > 192) poll_loops = 192; if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_BEFORE_WAIT) { uint32_t before_loops = loops * 2 + 8; if (before_loops > poll_loops) before_loops = poll_loops; for (uint32_t i = 0; i < before_loops; i++) { (void)syz_fuse_send_poll_kh( notify_fd, release_poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } } if (release_poll_pid > 0) syz_fuse_wait_or_kill(release_poll_pid); if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL_DELAY) { int delay_ms = wait_ms; if (delay_ms > 128) delay_ms = 128; syz_fuse_sleep_ms(delay_ms); } for (uint32_t i = 0; i < poll_loops; i++) { (void)syz_fuse_send_poll_kh( notify_fd, release_poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_ABORT) { syz_fuse_kmsg2("cleanup_postrelease_abort", release_poll_kh, poll_loops); syz_fuse_abort_all_connections_now( "cleanup_postrelease_abort"); } if (notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_PRESSURE) syz_fuse_edge_memory_pressure( "cleanup_postrelease_pressure", loops); poll_kh = release_poll_kh; poll_seen = 1; } else if (release_poll_pid > 0) { syz_fuse_wait_or_kill(release_poll_pid); } } } cleanup: if (have_thread) (void)pthread_join(thr, NULL); if (have_edge_thread) (void)pthread_join(edge_thr, NULL); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); if (live_hold_pid > 0) syz_fuse_wait_or_kill(live_hold_pid); if (poll_pid > 0) syz_fuse_wait_or_kill(poll_pid); if (materialize_pid > 0) syz_fuse_wait_or_kill(materialize_pid); if (poststore_fault_pid > 0) syz_fuse_wait_or_kill(poststore_fault_pid); if (postabort_fault_pid > 0) syz_fuse_wait_or_kill(postabort_fault_pid); if ((notify_magic & SYZ_FUSE_CR_MAGIC_POST_RELEASE_POLL) && notify_fd >= 0 && poll_kh) { uint32_t poll_loops = loops * 4 + 16; if (poll_loops > 192) poll_loops = 192; for (uint32_t i = 0; i < poll_loops; i++) { (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if ((i & 7) == 7) syz_fuse_sleep_ms(1); } if ((notify_magic & SYZ_FUSE_CR_MAGIC_FINAL_POSTPOLL_FAULT) && mountpoint) { pid_t final_fault_pid; uint32_t final_fault_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; size_t final_map_len = store_size > 65536 ? store_size : 65536; if (mode & SYZ_FUSE_CR_POLL_STORM) final_fault_mode |= SYZ_FUSE_SR_CHILD_POLL_LOOP; syz_fuse_kmsg2("cleanup_final_postpoll_fault_start", poll_kh, poll_loops); final_fault_pid = fork(); if (final_fault_pid == 0) { syz_fuse_notify_sink_live_hold_child( mountpoint, loops + 96, final_fault_mode, final_map_len); _exit(0); } if (read_fd >= 0) { for (uint32_t i = 0; i < loops / 2 + 4; i++) { (void)syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_cleanup_race_fault_mode(mode), wait_ms); if ((i & 3) == 3) syz_fuse_sleep_ms(1); } } if (final_fault_pid > 0) syz_fuse_wait_or_kill(final_fault_pid); syz_fuse_kmsg2("cleanup_final_postpoll_fault_done", poll_kh, final_fault_pid); } } if ((mode & SYZ_FUSE_CR_DRAIN_AFTER) && read_fd >= 0) { uint32_t drain_loops = loops * 4 + 8; if (drain_loops > 160) drain_loops = 160; for (uint32_t i = 0; i < drain_loops; i++) { int drain_ret; if (mode & SYZ_FUSE_CR_POLL_STORM) drain_ret = syz_fuse_handle_one_available_stateflip_capture_poll( read_fd, buf, buf_len, req_out, &st, wait_ms, &poll_kh, "syz_fuse_cleanup/final-drain-poll"); else drain_ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_cleanup/final-drain"); if (drain_ret < 0) break; if (drain_ret == 2 && notify_fd >= 0 && poll_kh) (void)syz_fuse_send_poll_kh(notify_fd, poll_kh); if (drain_ret > 0 && i >= loops) break; } } if (notifyreply_splice_done) { syz_fuse_kmsg2("cleanup_notifyreply_pipe_post_ops", notifyreply_pipe[0], notifyreply_pipe[1]); syz_fuse_post_teardown_pipe_ops( notifyreply_pipe, SYZ_FUSE_SR_PIPE_DRAIN_AFTER_TEARDOWN | SYZ_FUSE_SR_PIPE_TEE_AFTER_TEARDOWN); } syz_fuse_close_pipe(notifyreply_pipe); if (high_pinned_fd >= 0) (void)close(high_pinned_fd); if (clone_fd >= 0) (void)close(clone_fd); #undef SYZ_FUSE_CLEANUP_SEND_STORE return ret; } static uint32_t syz_fuse_notify_sink_fault_mode(uint32_t mode) { uint32_t ret = 0; if (mode & SYZ_FUSE_NSP_FAULT_MPROTECT) ret |= SYZ_FUSE_FR_FAULT_MPROTECT; if (mode & SYZ_FUSE_NSP_FAULT_MUNMAP) ret |= SYZ_FUSE_FR_FAULT_MUNMAP; if (mode & SYZ_FUSE_NSP_READV_SPLIT) ret |= SYZ_FUSE_FR_READV_SPLIT; return ret; } static int syz_fuse_notify_sink_drain( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller) { int ret = 0; if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, st, wait_ms, caller); if (ret < 0) return -1; if (ret > 0 && i >= loops / 3) break; } return 0; } static int syz_fuse_notify_sink_drain_child_window( pid_t pid, int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller) { int status = 0; if (pid <= 0) return 1; if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { if (waitpid(pid, &status, WNOHANG) == pid) { syz_fuse_kmsg2("nsp_child_window_reaped", status, pid); return 1; } if (read_fd >= 0) (void)syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, st, wait_ms, caller); syz_fuse_sleep_ms(5); } if (waitpid(pid, &status, WNOHANG) == pid) { syz_fuse_kmsg2("nsp_child_window_reaped", status, pid); return 1; } syz_fuse_kmsg2("nsp_child_window_alive", loops, pid); return 0; } struct syz_fuse_captured_req { uint32_t opcode; uint32_t len; uint32_t aux_size; uint64_t unique; uint64_t nodeid; }; static int syz_fuse_notify_sink_read_processing_no_reply_capture( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller, struct syz_fuse_captured_req* captured) { struct fuse_out_header* out_hdr = NULL; if (read_fd < 0 || !buf || buf_len < FUSE_MIN_READ_BUFFER || !req_out || !st) return -1; if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { const struct fuse_in_header* in_hdr; int pick; int ret; if (syz_fuse_wait_readable(read_fd, wait_ms) < 0) { syz_fuse_sleep_ms(1); continue; } ret = read(read_fd, buf, buf_len); if (ret < (int)sizeof(struct fuse_in_header)) continue; in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) continue; syz_fuse_kmsg2(caller, in_hdr->opcode, in_hdr->len); syz_fuse_kmsg2("nsp_clone_processing_no_reply_unique", in_hdr->unique, in_hdr->nodeid); if (in_hdr->opcode == FUSE_GETATTR || in_hdr->opcode == FUSE_OPEN || in_hdr->opcode == FUSE_READ || in_hdr->opcode == FUSE_WRITE || in_hdr->opcode == FUSE_POLL) { syz_fuse_kmsg2("nsp_clone_processing_no_reply_capture", in_hdr->opcode, in_hdr->unique); if (captured) { captured->opcode = in_hdr->opcode; captured->len = in_hdr->len; captured->aux_size = 0; if ((in_hdr->opcode == FUSE_READ || in_hdr->opcode == FUSE_WRITE) && in_hdr->len >= sizeof(*in_hdr) + 20) captured->aux_size = *(uint32_t*)((char*)in_hdr + sizeof(*in_hdr) + 16); captured->unique = in_hdr->unique; captured->nodeid = in_hdr->nodeid; } return in_hdr->opcode; } if (in_hdr->opcode == FUSE_NOTIFY_REPLY || in_hdr->opcode == FUSE_FORGET || in_hdr->opcode == FUSE_BATCH_FORGET || in_hdr->opcode == FUSE_INTERRUPT) { continue; } pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick == 0 && out_hdr) { syz_fuse_force_stateful_reply_success(out_hdr, in_hdr->opcode); (void)fuse_send_response(read_fd, in_hdr, out_hdr); } } return 0; } static int __attribute__((unused)) syz_fuse_notify_sink_read_processing_no_reply( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller) { return syz_fuse_notify_sink_read_processing_no_reply_capture( read_fd, buf, buf_len, req_out, st, wait_ms, loops, caller, NULL); } static int syz_fuse_send_sized_read_response(int fd, uint64_t unique, uint32_t size) { struct fuse_out_header out_hdr; struct iovec iov[2]; char* payload = NULL; size_t map_len; int ret; int saved_errno; if (fd < 0 || !unique) return -1; if (size > 1048576) size = 1048576; map_len = (size + 4095) & ~(size_t)4095; if (size) { payload = (char*)mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (payload == MAP_FAILED) return -1; memset(payload, 0x71, size); } memset(&out_hdr, 0, sizeof(out_hdr)); out_hdr.len = sizeof(out_hdr) + size; out_hdr.error = 0; out_hdr.unique = unique; iov[0].iov_base = &out_hdr; iov[0].iov_len = sizeof(out_hdr); iov[1].iov_base = payload; iov[1].iov_len = size; syz_fuse_kmsg2("nsp_read_skip_full_reply", size, unique); ret = writev(fd, iov, size ? 2 : 1); saved_errno = errno; syz_fuse_kmsg2("nsp_read_skip_full_reply_done", ret < 0 ? -1 : ret, ret < 0 ? saved_errno : 0); if (payload) (void)munmap(payload, map_len); errno = saved_errno; return ret; } static int __attribute__((unused)) syz_fuse_notify_sink_read_processing_prefer_read_capture( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller, struct syz_fuse_captured_req* captured) { struct syz_fuse_captured_req fallback; struct fuse_out_header* out_hdr = NULL; int have_fallback = 0; memset(&fallback, 0, sizeof(fallback)); if (read_fd < 0 || !buf || buf_len < FUSE_MIN_READ_BUFFER || !req_out || !st) return -1; if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { const struct fuse_in_header* in_hdr; int pick; int ret; if (syz_fuse_wait_readable(read_fd, wait_ms) < 0) { syz_fuse_sleep_ms(1); continue; } ret = read(read_fd, buf, buf_len); if (ret < (int)sizeof(struct fuse_in_header)) continue; in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) continue; syz_fuse_kmsg2(caller, in_hdr->opcode, in_hdr->len); syz_fuse_kmsg2("nsp_prefer_read_unique", in_hdr->unique, in_hdr->nodeid); if (in_hdr->opcode == FUSE_READ) { uint32_t read_size = 0; if (in_hdr->len >= sizeof(*in_hdr) + 20) read_size = *(uint32_t*)((char*)in_hdr + sizeof(*in_hdr) + 16); if (read_size < 65536) { syz_fuse_kmsg2("nsp_prefer_read_skip_grow", read_size, in_hdr->unique); (void)syz_fuse_send_sized_read_response( read_fd, in_hdr->unique, read_size); continue; } if (captured) { captured->opcode = in_hdr->opcode; captured->len = in_hdr->len; captured->aux_size = 0; if (in_hdr->len >= sizeof(*in_hdr) + 20) captured->aux_size = *(uint32_t*)((char*)in_hdr + sizeof(*in_hdr) + 16); captured->unique = in_hdr->unique; captured->nodeid = in_hdr->nodeid; } syz_fuse_kmsg2("nsp_prefer_read_size", read_size, in_hdr->len); syz_fuse_kmsg2("nsp_prefer_read_capture", in_hdr->opcode, in_hdr->unique); return in_hdr->opcode; } if (in_hdr->opcode == FUSE_WRITE && !have_fallback) { fallback.opcode = in_hdr->opcode; fallback.len = in_hdr->len; fallback.aux_size = 0; if (in_hdr->len >= sizeof(*in_hdr) + 20) fallback.aux_size = *(uint32_t*)((char*)in_hdr + sizeof(*in_hdr) + 16); fallback.unique = in_hdr->unique; fallback.nodeid = in_hdr->nodeid; have_fallback = 1; syz_fuse_kmsg2("nsp_prefer_read_fallback_write", in_hdr->opcode, in_hdr->unique); continue; } if (in_hdr->opcode == FUSE_NOTIFY_REPLY || in_hdr->opcode == FUSE_FORGET || in_hdr->opcode == FUSE_BATCH_FORGET || in_hdr->opcode == FUSE_INTERRUPT) { continue; } pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, caller); if (pick == 0 && out_hdr) { syz_fuse_force_stateful_reply_success(out_hdr, in_hdr->opcode); (void)fuse_send_response(read_fd, in_hdr, out_hdr); } } if (have_fallback) { if (captured) *captured = fallback; syz_fuse_kmsg2("nsp_prefer_read_return_fallback", fallback.opcode, fallback.unique); return fallback.opcode; } return 0; } struct syz_fuse_faulting_reply_arg { int fd; uint32_t opcode; uint32_t aux_size; uint64_t unique; uint32_t delay_ms; int ret; int err; }; struct syz_fuse_splice_abort_arg { uint32_t delay_us; uint32_t rounds; uint32_t pause_us; int* abort_fds; uint32_t abort_nfds; volatile uint32_t* go; volatile uint32_t* ready; uint64_t affinity_mask; int target_fd; }; static void syz_fuse_busy_delay_us(uint32_t delay_us) { struct timespec start; struct timespec now; uint64_t start_ns; uint64_t now_ns; uint64_t want_ns; if (!delay_us) return; if (clock_gettime(CLOCK_MONOTONIC, &start)) return; start_ns = (uint64_t)start.tv_sec * 1000000000ULL + start.tv_nsec; want_ns = (uint64_t)delay_us * 1000ULL; do { if (clock_gettime(CLOCK_MONOTONIC, &now)) break; now_ns = (uint64_t)now.tv_sec * 1000000000ULL + now.tv_nsec; } while (now_ns - start_ns < want_ns); } static void* syz_fuse_splice_abort_thread(void* data) { struct syz_fuse_splice_abort_arg* arg = (struct syz_fuse_splice_abort_arg*)data; uint32_t rounds = arg->rounds ? arg->rounds : 1; if (arg->affinity_mask) (void)syscall(__NR_sched_setaffinity, 0, sizeof(arg->affinity_mask), &arg->affinity_mask); if (arg->go) { if (arg->ready) *arg->ready = 1; while (!*arg->go) { /* * Keep this as a real spin: trace showed that pthread * scheduling alone misses the sub-millisecond splice * page-steal window. */ } if (arg->delay_us) syz_fuse_busy_delay_us(arg->delay_us); } else if (arg->delay_us) { if (arg->delay_us <= 2000) syz_fuse_busy_delay_us(arg->delay_us); else usleep(arg->delay_us); } for (uint32_t i = 0; i < rounds; i++) { uint32_t ok = 0; uint32_t fail = 0; syz_fuse_kmsg2("nsp_splice_reply_inside_abort_round", i, rounds); if (i == 0 && arg->target_fd >= 0) { int close_ret = close(arg->target_fd); int close_err = errno; syz_fuse_kmsg2("nsp_splice_reply_close_reply_fd", close_ret, close_ret < 0 ? close_err : 0); arg->target_fd = -1; } if (arg->abort_fds && arg->abort_nfds) { for (uint32_t j = 0; j < arg->abort_nfds; j++) { if (arg->abort_fds[j] >= 0 && write(arg->abort_fds[j], "1", 1) == 1) ok++; else fail++; } syz_fuse_kmsg2("nsp_splice_reply_fast_abort", ok, fail); } else { syz_fuse_abort_all_connections_now( "nsp_splice_reply_inside_abort"); } if (arg->pause_us) usleep(arg->pause_us); else sched_yield(); } return NULL; } static int syz_fuse_send_faulting_captured_reply(int fd, uint32_t opcode, uint64_t unique, uint32_t aux_size, volatile uint32_t* abort_go) { struct fuse_out_header out_hdr; struct iovec iov[3]; void* fault_page; char* prefix = NULL; size_t prefix_len = 0; size_t fault_len = 8; size_t payload_len = 8; size_t prefix_map_len = 0; int iovcnt = 0; int ret; int saved_errno; if (fd < 0 || !unique) return -1; if (opcode == FUSE_READ) { payload_len = aux_size ? aux_size : 65536; if (payload_len > 1048576) payload_len = 1048576; if (payload_len < 1) payload_len = 1; fault_len = payload_len > 4096 ? 4096 : payload_len; prefix_len = payload_len - fault_len; } else if (opcode == FUSE_WRITE) { payload_len = 8; fault_len = 8; prefix_len = 0; } else { return -1; } if (prefix_len) { prefix_map_len = (prefix_len + 4095) & ~(size_t)4095; prefix = (char*)mmap(NULL, prefix_map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (prefix == MAP_FAILED) prefix = NULL; else { memset(prefix, 0x43, prefix_len); (void)madvise(prefix, prefix_map_len, MADV_DONTNEED); syz_fuse_kmsg2("nsp_faulting_reply_prefix_dontneed", prefix_len, prefix_map_len); } if (!prefix) { prefix_len = 0; fault_len = payload_len; } } fault_page = mmap(NULL, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (fault_page == MAP_FAILED) fault_page = (void*)1; memset(&out_hdr, 0, sizeof(out_hdr)); out_hdr.len = sizeof(out_hdr) + prefix_len + fault_len; out_hdr.error = 0; out_hdr.unique = unique; iov[iovcnt].iov_base = &out_hdr; iov[iovcnt].iov_len = sizeof(out_hdr); iovcnt++; if (prefix_len) { iov[iovcnt].iov_base = prefix; iov[iovcnt].iov_len = prefix_len; iovcnt++; } iov[iovcnt].iov_base = fault_page; iov[iovcnt].iov_len = fault_len; iovcnt++; syz_fuse_kmsg2("nsp_faulting_reply_submit", opcode, unique); syz_fuse_kmsg2("nsp_faulting_reply_shape", prefix_len, fault_len); if (opcode == FUSE_READ) syz_fuse_kmsg2("nsp_faulting_read_reply_submit", fd, unique); else syz_fuse_kmsg2("nsp_faulting_write_reply_submit", fd, unique); if (abort_go) { *abort_go = 1; if (opcode == FUSE_READ) { syz_fuse_kmsg2("nsp_faulting_reply_prewrite_delay", opcode, 350); syz_fuse_busy_delay_us(350); } } ret = writev(fd, iov, iovcnt); saved_errno = errno; syz_fuse_kmsg2("nsp_faulting_reply_done", ret, ret < 0 ? saved_errno : 0); if (opcode == FUSE_READ) syz_fuse_kmsg2("nsp_faulting_read_reply_done", ret, ret < 0 ? saved_errno : 0); else syz_fuse_kmsg2("nsp_faulting_write_reply_done", ret, ret < 0 ? saved_errno : 0); if (fault_page != (void*)1) (void)munmap(fault_page, 4096); if (prefix) (void)munmap(prefix, prefix_map_len); errno = saved_errno; return ret; } struct syz_fuse_pipe_writer_arg { int fd; char* payload; size_t len; ssize_t ret; int err; }; static void* syz_fuse_pipe_writer_thread(void* data) { struct syz_fuse_pipe_writer_arg* arg = (struct syz_fuse_pipe_writer_arg*)data; size_t off = 0; arg->ret = 0; arg->err = 0; (void)signal(SIGPIPE, SIG_IGN); syz_fuse_kmsg2("nsp_splice_reply_stream_payload_start", arg->len, arg->fd); while (off < arg->len) { ssize_t n = write(arg->fd, arg->payload + off, arg->len - off); if (n < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { sched_yield(); continue; } arg->err = errno; arg->ret = -1; syz_fuse_kmsg2("nsp_splice_reply_stream_payload_err", -errno, off); return NULL; } if (n == 0) { arg->err = EIO; arg->ret = -1; syz_fuse_kmsg2("nsp_splice_reply_stream_payload_zero", off, arg->len); return NULL; } off += n; arg->ret = off; } syz_fuse_kmsg2("nsp_splice_reply_stream_payload_done", arg->ret, arg->err); return NULL; } static int __attribute__((unused)) syz_fuse_send_splice_captured_reply( int fd, uint32_t opcode, uint64_t unique, uint32_t aux_size, uint32_t wait_ms, uint32_t loops) { struct fuse_out_header out_hdr; struct iovec iov; char* payload; size_t payload_len; size_t map_len; size_t total_len; ssize_t written; ssize_t spliced = -1; int pipefds[2] = {-1, -1}; int payload_file_fd = -1; int saved_errno = 0; struct syz_fuse_splice_abort_arg abort_arg; pthread_t abort_th; pthread_t payload_th; int have_abort_th = 0; int have_payload_th = 0; int payload_from_write = 0; int stream_payload = 0; int pipe_capacity = -1; int abort_fds[16]; uint32_t abort_nfds = 0; volatile uint32_t abort_go = 0; volatile uint32_t abort_ready = 0; uint64_t main_affinity = 2; uint32_t presplice_delay_us = 700; uint32_t abort_rounds = 32; struct syz_fuse_pipe_writer_arg payload_arg; for (uint32_t i = 0; i < 16; i++) abort_fds[i] = -1; if (fd < 0 || !unique || opcode != FUSE_READ) return -1; payload_len = aux_size ? aux_size : 65536; if (wait_ms <= 100) presplice_delay_us = wait_ms * 28; if (loops) abort_rounds = loops; if (abort_rounds < 8) abort_rounds = 8; if (abort_rounds > 128) abort_rounds = 128; if (payload_len > 1048576) payload_len = 1048576; if (payload_len < 1) payload_len = 1; map_len = (payload_len + 4095) & ~(size_t)4095; total_len = sizeof(out_hdr) + payload_len; payload = (char*)mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (payload == MAP_FAILED) return -1; memset(payload, 0x52, payload_len); memset(&out_hdr, 0, sizeof(out_hdr)); out_hdr.len = total_len; out_hdr.error = 0; out_hdr.unique = unique; if (pipe(pipefds) < 0) goto out_map; { int pipe_set_ret = fcntl(pipefds[1], F_SETPIPE_SZ, 2097152); int pipe_set_err = errno; syz_fuse_kmsg2("nsp_splice_reply_pipe_set", pipe_set_ret < 0 ? -pipe_set_err : pipe_set_ret, 2097152); if (pipe_set_ret < 0) { pipe_set_ret = fcntl(pipefds[1], F_SETPIPE_SZ, 131072); pipe_set_err = errno; syz_fuse_kmsg2("nsp_splice_reply_pipe_set_fallback", pipe_set_ret < 0 ? -pipe_set_err : pipe_set_ret, 131072); } } pipe_capacity = fcntl(pipefds[1], F_GETPIPE_SZ); syz_fuse_kmsg2("nsp_splice_reply_pipe_size", pipe_capacity, total_len); if (pipe_capacity > (int)sizeof(out_hdr) && (size_t)pipe_capacity < total_len) { size_t clamped_payload = (size_t)pipe_capacity - sizeof(out_hdr); if (clamped_payload >= 4096) { syz_fuse_kmsg2("nsp_splice_reply_pipe_clamp", payload_len, clamped_payload); payload_len = clamped_payload; total_len = sizeof(out_hdr) + payload_len; out_hdr.len = total_len; } } if (pipe_capacity > 0 && (size_t)pipe_capacity < total_len) stream_payload = 1; written = write(pipefds[1], &out_hdr, sizeof(out_hdr)); syz_fuse_kmsg2("nsp_splice_reply_header", written < 0 ? -errno : written, sizeof(out_hdr)); if (written != (ssize_t)sizeof(out_hdr)) goto out_pipe; iov.iov_base = payload; iov.iov_len = payload_len; if (payload_len <= 1048576 && payload_len == 0) { char payload_path[96]; ssize_t file_written; loff_t payload_off = 0; static const char* const clean_payload_paths[] = { "/usr/bin/systemctl", "/usr/bin/openssl", "/usr/bin/python3.12", "/tmp/syzkaller-current/bin/linux_amd64/syz-executor", NULL, }; /* * Anonymous pipe buffers make fuse_try_move_folio() extremely * fast: pipe_buf_confirm() is a no-op and anon_pipe_buf_try_steal() * only checks the page refcount. Feed the payload through a regular * file first so FUSE receives page_cache_pipe_buf_ops buffers. That * forces page-cache confirm/steal/release code while the FUSE request * is unlocked, which is the lifetime window we want to stress. */ for (uint32_t i = 0; clean_payload_paths[i] && payload_from_write != 3; i++) { payload_off = 0; payload_file_fd = open(clean_payload_paths[i], O_RDONLY); if (payload_file_fd < 0) { syz_fuse_kmsg2("nsp_splice_reply_clean_open", -errno, i); continue; } written = syscall(__NR_splice, payload_file_fd, &payload_off, pipefds[1], NULL, payload_len, SPLICE_F_MOVE | SPLICE_F_MORE); syz_fuse_kmsg2("nsp_splice_reply_clean_payload", written < 0 ? -errno : written, i); if (written == (ssize_t)payload_len) payload_from_write = 3; else { close(payload_file_fd); payload_file_fd = -1; } } if (!payload_from_write) { snprintf(payload_path, sizeof(payload_path), "syz-fuse-payload-%ld-%llu", (long)getpid(), (unsigned long long)unique); payload_file_fd = open(payload_path, O_CREAT | O_RDWR | O_TRUNC, 0600); if (payload_file_fd >= 0) { (void)unlink(payload_path); file_written = write(payload_file_fd, payload, payload_len); syz_fuse_kmsg2("nsp_splice_reply_file_fill", file_written < 0 ? -errno : file_written, payload_len); if (file_written == (ssize_t)payload_len) { (void)fsync(payload_file_fd); (void)lseek(payload_file_fd, 0, SEEK_SET); written = syscall(__NR_splice, payload_file_fd, &payload_off, pipefds[1], NULL, payload_len, SPLICE_F_MOVE | SPLICE_F_MORE); syz_fuse_kmsg2( "nsp_splice_reply_file_payload", written < 0 ? -errno : written, payload_len); if (written == (ssize_t)payload_len) payload_from_write = 2; } } else { syz_fuse_kmsg2("nsp_splice_reply_file_open", -errno, payload_len); } } else { } if (payload_from_write != 2 && payload_from_write != 3) { if (payload_file_fd >= 0) { close(payload_file_fd); payload_file_fd = -1; } payload_from_write = 1; written = write(pipefds[1], payload, payload_len); syz_fuse_kmsg2("nsp_splice_reply_write_payload", written < 0 ? -errno : written, payload_len); } } else if (payload_len <= 1048576 && !stream_payload) { payload_from_write = 1; written = write(pipefds[1], payload, payload_len); syz_fuse_kmsg2("nsp_splice_reply_write_payload", written < 0 ? -errno : written, payload_len); } else if (payload_len <= 1048576) { payload_from_write = 4; (void)fcntl(pipefds[1], F_SETFL, fcntl(pipefds[1], F_GETFL, 0) | O_NONBLOCK); memset(&payload_arg, 0, sizeof(payload_arg)); payload_arg.fd = pipefds[1]; payload_arg.payload = payload; payload_arg.len = payload_len; have_payload_th = pthread_create(&payload_th, NULL, syz_fuse_pipe_writer_thread, &payload_arg) == 0; syz_fuse_kmsg2("nsp_splice_reply_stream_payload_arm", have_payload_th, payload_len); if (!have_payload_th) goto out_pipe; written = payload_len; } else { written = syscall(__NR_vmsplice, pipefds[1], &iov, 1, SPLICE_F_GIFT); syz_fuse_kmsg2("nsp_splice_reply_vmsplice", written < 0 ? -errno : written, payload_len); } if (payload_from_write != 4 && written != (ssize_t)payload_len) goto out_pipe; syz_fuse_kmsg2("nsp_splice_reply_disturb", payload_len, map_len); if (!payload_from_write) (void)mprotect(payload, map_len, PROT_NONE); if (payload_from_write != 4) { (void)munmap(payload, map_len); payload = NULL; } syz_fuse_kmsg2("nsp_splice_reply_submit", fd, unique); memset(&abort_arg, 0, sizeof(abort_arg)); abort_arg.delay_us = payload_from_write == 4 ? 100 : 0; abort_arg.rounds = abort_rounds; abort_arg.pause_us = 0; abort_nfds = syz_fuse_open_abort_fds( abort_fds, 16, "nsp_splice_reply_preopen_abort"); abort_arg.abort_fds = abort_fds; abort_arg.abort_nfds = abort_nfds; abort_arg.go = &abort_go; abort_arg.ready = &abort_ready; abort_arg.affinity_mask = 1; abort_arg.target_fd = -1; syz_fuse_kmsg2("nsp_splice_reply_inside_abort_arm", abort_arg.rounds, abort_nfds); syz_fuse_kmsg2("nsp_splice_reply_affinity", abort_arg.affinity_mask, main_affinity); (void)syscall(__NR_sched_setaffinity, 0, sizeof(main_affinity), &main_affinity); have_abort_th = pthread_create(&abort_th, NULL, syz_fuse_splice_abort_thread, &abort_arg) == 0; if (have_abort_th) { for (uint32_t i = 0; i < 1000000 && !abort_ready; i++) { if ((i & 0xfff) == 0) sched_yield(); } syz_fuse_kmsg2("nsp_splice_reply_abort_ready", abort_ready, abort_nfds); } abort_go = 1; if (payload_from_write == 4) { syz_fuse_kmsg2("nsp_splice_reply_presplice_delay", opcode, 0); } else { syz_fuse_kmsg2("nsp_splice_reply_presplice_delay", opcode, presplice_delay_us); if (presplice_delay_us) syz_fuse_busy_delay_us(presplice_delay_us); } spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, total_len, SPLICE_F_MOVE | SPLICE_F_MORE); saved_errno = errno; if (spliced < 0 && saved_errno == EINVAL) { spliced = syscall(__NR_splice, pipefds[0], NULL, fd, NULL, total_len, 0); saved_errno = errno; } syz_fuse_kmsg2("nsp_splice_reply_done", spliced < 0 ? -1 : spliced, spliced < 0 ? saved_errno : 0); if (have_payload_th) { if (pipefds[0] >= 0) { close(pipefds[0]); pipefds[0] = -1; } pthread_join(payload_th, NULL); have_payload_th = 0; syz_fuse_kmsg2("nsp_splice_reply_stream_payload_join", payload_arg.ret, payload_arg.err); } if (have_abort_th) pthread_join(abort_th, NULL); syz_fuse_close_abort_fds(abort_fds, abort_nfds); out_pipe: if (have_payload_th) { if (pipefds[0] >= 0) { close(pipefds[0]); pipefds[0] = -1; } pthread_join(payload_th, NULL); } syz_fuse_close_abort_fds(abort_fds, abort_nfds); if (payload_file_fd >= 0) close(payload_file_fd); if (pipefds[0] >= 0) close(pipefds[0]); if (pipefds[1] >= 0) close(pipefds[1]); out_map: if (payload && payload != MAP_FAILED) (void)munmap(payload, map_len); errno = saved_errno; return spliced < 0 ? -1 : (int)spliced; } static void* syz_fuse_faulting_reply_thread(void* data) { struct syz_fuse_faulting_reply_arg* arg = (struct syz_fuse_faulting_reply_arg*)data; if (arg->delay_ms) syz_fuse_sleep_ms(arg->delay_ms); arg->ret = syz_fuse_send_faulting_captured_reply( arg->fd, arg->opcode, arg->unique, arg->aux_size, NULL); arg->err = errno; return NULL; } static void syz_fuse_faulting_reply_abort_race(int fd, const struct syz_fuse_captured_req* cap, uint32_t wait_ms, uint32_t loops) { struct syz_fuse_faulting_reply_arg arg; pthread_t th; int have_thread; uint32_t rounds = loops ? loops : 1; uint32_t first_abort_delay = 4; uint32_t first_abort_delay_us = 0; if (fd < 0 || !cap || !cap->unique) return; memset(&arg, 0, sizeof(arg)); arg.fd = fd; arg.opcode = cap->opcode; arg.aux_size = cap->aux_size; arg.unique = cap->unique; arg.delay_ms = 0; if (cap->opcode == FUSE_READ) { /* * For faulting READ replies the useful window is the partial * fuse_dev_write() copy before the final -EFAULT. The old READ * delay was tuned for splice and arrives after fuse_request_end(). */ first_abort_delay = 0; first_abort_delay_us = 0; if (rounds < 32) rounds = 32; if (rounds > 64) rounds = 64; } else { first_abort_delay = 4; if (rounds > 8) rounds = 8; } syz_fuse_kmsg2("nsp_faulting_reply_abort_race_start", fd, cap->unique); syz_fuse_kmsg2("nsp_faulting_reply_abort_delay", cap->opcode, first_abort_delay_us ? first_abort_delay_us : first_abort_delay); if (cap->opcode == FUSE_READ) { arg.ret = syz_fuse_send_splice_captured_reply( arg.fd, arg.opcode, arg.unique, arg.aux_size, wait_ms, loops); arg.err = errno; syz_fuse_kmsg2("nsp_splice_reply_abort_race_done", arg.ret, arg.err); return; } have_thread = pthread_create(&th, NULL, syz_fuse_faulting_reply_thread, &arg) == 0; for (uint32_t i = 0; i < rounds; i++) { if (i == 0 && wait_ms > 1) { if (first_abort_delay_us) usleep(first_abort_delay_us); else syz_fuse_sleep_ms(first_abort_delay); } syz_fuse_abort_all_connections_now("nsp_faulting_reply_abort"); if ((i & 1) == 0) syz_fuse_edge_memory_pressure( "nsp_faulting_reply_pressure", 1); } if (have_thread) (void)pthread_join(th, NULL); syz_fuse_kmsg2("nsp_faulting_reply_abort_race_done", arg.ret, arg.err); } static void syz_fuse_notify_sink_best_effort_drain( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, const char* caller) { if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { if (read_fd >= 0) (void)syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, st, wait_ms, caller); syz_fuse_sleep_ms(1); } } static void syz_fuse_notify_sink_touch_marker(int writable, size_t map_len) { syz_fuse_kmsg2("nsp_delayed_fault_before_touch", writable, map_len); } static uint32_t syz_fuse_notify_sink_want_lookup_mask(uint32_t mode) { uint32_t mask = 0; if (mode & (SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_EXPIRE_FILE)) mask |= SYZ_FUSE_LOOKUP_HIT_FILE; if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) mask |= SYZ_FUSE_LOOKUP_HIT_ALIAS; if (mode & SYZ_FUSE_NSP_DELETE_DIR) mask |= SYZ_FUSE_LOOKUP_HIT_DIR; if (mode & SYZ_FUSE_NSP_PRUNE_ALIAS) mask |= SYZ_FUSE_LOOKUP_HIT_FILE | SYZ_FUSE_LOOKUP_HIT_DIR; if (mode & SYZ_FUSE_NSP_VALID_PRUNE_BATCH) mask |= SYZ_FUSE_LOOKUP_HIT_FILE | SYZ_FUSE_LOOKUP_HIT_DIR | SYZ_FUSE_LOOKUP_HIT_CHILD; if (!mask) mask = SYZ_FUSE_LOOKUP_HIT_FILE; return mask; } static int syz_fuse_notify_sink_drain_until_lookup( int read_fd, char* buf, int buf_len, struct syz_fuse_req_out* req_out, struct syz_fuse_stateflip_state* st, int wait_ms, uint32_t loops, uint32_t want_mask, const char* caller) { int ret = 0; if (!loops) loops = 1; for (uint32_t i = 0; i < loops; i++) { if ((st->lookup_seen_mask & want_mask) == want_mask) return 0; ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, st, wait_ms, caller); if (ret < 0) return -1; if (ret > 0) syz_fuse_sleep_ms(1); } return 0; } static void __attribute__((unused)) syz_fuse_notify_sink_path_storm(const char* mountpoint, uint32_t loops) { char file_path[512]; char alias_path[512]; char dir_path[512]; char child_path[512]; char child2_path[512]; char subdir_path[512]; struct stat st; if (!mountpoint) return; if (!loops) loops = 4; if (loops > 32) loops = 32; syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path(mountpoint, "dir", dir_path, sizeof(dir_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); syz_fuse_join_path2(mountpoint, "dir", "b", child2_path, sizeof(child2_path)); syz_fuse_join_path2(mountpoint, "dir", "sub", subdir_path, sizeof(subdir_path)); for (uint32_t i = 0; i < loops; i++) { (void)fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW); int stat_alias_ret = fstatat(AT_FDCWD, alias_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_alias", stat_alias_ret, stat_alias_ret < 0 ? errno : 0); int stat_dir_ret = fstatat(AT_FDCWD, dir_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_dir", stat_dir_ret, stat_dir_ret < 0 ? errno : 0); int stat_child_ret = fstatat(AT_FDCWD, child_path, &st, AT_SYMLINK_NOFOLLOW); if (i == 0) syz_fuse_kmsg2("stateflip_fstat_child", stat_child_ret, stat_child_ret < 0 ? errno : 0); (void)fstatat(AT_FDCWD, child2_path, &st, AT_SYMLINK_NOFOLLOW); (void)rename(file_path, alias_path); (void)rename(alias_path, file_path); (void)unlink(alias_path); (void)unlink(child2_path); (void)rmdir(subdir_path); (void)rmdir(dir_path); syz_fuse_sleep_ms(1); } } static void syz_fuse_notify_sink_direct_read_child(const char* mountpoint, uint32_t loops, size_t read_len, uint32_t delay_ms) { char file_path[512]; char alias_path[512]; char child_path[512]; const char* paths[3]; char tmp[4096]; char* read_buf = tmp; size_t read_buf_len = sizeof(tmp); int fd = -1; int open_errno = 0; if (!mountpoint) return; if (!loops) loops = 8; if (loops > 96) loops = 96; if (read_len < sizeof(tmp)) read_len = sizeof(tmp); if (read_len > 1048576) read_len = 1048576; if (read_len > sizeof(tmp)) { read_buf_len = (read_len + 4095) & ~(size_t)4095; read_buf = (char*)mmap(NULL, read_buf_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (read_buf == MAP_FAILED) { read_buf = tmp; read_buf_len = sizeof(tmp); read_len = sizeof(tmp); } } syz_fuse_kmsg2("nsp_direct_read_size", read_len, read_buf_len); syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); paths[0] = file_path; paths[1] = alias_path; paths[2] = child_path; for (int i = 0; i < 3 && fd < 0; i++) { struct stat st; int stat_ret = fstatat(AT_FDCWD, paths[i], &st, AT_SYMLINK_NOFOLLOW); syz_fuse_kmsg2("nsp_direct_read_stat_path", stat_ret, stat_ret < 0 ? errno : i); fd = open(paths[i], O_RDWR | O_NONBLOCK | O_CLOEXEC); open_errno = errno; syz_fuse_kmsg2("nsp_direct_read_open_rw", fd, fd < 0 ? open_errno : i); if (fd >= 0) break; fd = open(paths[i], O_RDONLY | O_NONBLOCK | O_CLOEXEC); open_errno = errno; syz_fuse_kmsg2("nsp_direct_read_open_ro_nb", fd, fd < 0 ? open_errno : i); if (fd >= 0) break; fd = open(paths[i], O_RDONLY | O_CLOEXEC); open_errno = errno; syz_fuse_kmsg2("nsp_direct_read_open_ro", fd, fd < 0 ? open_errno : i); } syz_fuse_kmsg2("nsp_direct_read_open", fd, fd < 0 ? open_errno : loops); if (fd < 0) return; { int adv1 = posix_fadvise(fd, 0, (off_t)read_len * 4, POSIX_FADV_SEQUENTIAL); int adv2 = posix_fadvise(fd, 0, (off_t)read_len * 4, POSIX_FADV_WILLNEED); long rah = syscall(__NR_readahead, fd, 0, read_len * 4); syz_fuse_kmsg2("nsp_buffered_read_readahead_hint", rah < 0 ? -errno : rah, read_len * 4); syz_fuse_kmsg2("nsp_buffered_read_fadvise", adv1, adv2); } if (delay_ms) { syz_fuse_kmsg2("nsp_direct_read_delay", delay_ms, loops); syz_fuse_sleep_ms(delay_ms); } for (uint32_t i = 0; i < loops; i++) { off_t off = (off_t)((i & 15) * 4096); ssize_t n = pread(fd, read_buf, read_len, off); if (i < 16 || (i & 15) == 0) syz_fuse_kmsg2("nsp_direct_read_iter", n, n < 0 ? errno : off); syz_fuse_sleep_ms(1); } close(fd); if (read_buf != tmp) (void)munmap(read_buf, read_buf_len); } static void syz_fuse_notify_sink_live_hold_child(const char* mountpoint, uint32_t loops, uint32_t fault_mode, size_t map_len) { char file_path[512]; char alias_path[512]; char dir_path[512]; char child_path[512]; char child2_path[512]; char subdir_path[512]; char subchild_path[512]; struct stat st; char tmp[64]; int root_fd = -1; int file_fd = -1; int alias_fd = -1; int dir_fd = -1; int child_fd = -1; int file_writable = 0; int file_open_logged = 0; int file_open_errno = 0; if (!mountpoint) return; if (!loops) loops = 8; if (loops > 160) loops = 160; syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path(mountpoint, "dir", dir_path, sizeof(dir_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); syz_fuse_join_path2(mountpoint, "dir", "b", child2_path, sizeof(child2_path)); syz_fuse_join_path2(mountpoint, "dir", "sub", subdir_path, sizeof(subdir_path)); snprintf(subchild_path, sizeof(subchild_path), "%s/%s/%s/%s", mountpoint, "dir", "sub", "c"); subchild_path[sizeof(subchild_path) - 1] = 0; for (uint32_t i = 0; i < loops; i++) { (void)fstatat(AT_FDCWD, mountpoint, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, alias_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, dir_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, child_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, child2_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, subdir_path, &st, AT_SYMLINK_NOFOLLOW); (void)fstatat(AT_FDCWD, subchild_path, &st, AT_SYMLINK_NOFOLLOW); if (root_fd < 0) root_fd = open(mountpoint, O_RDONLY | O_DIRECTORY | O_NONBLOCK | O_CLOEXEC); if (file_fd < 0) file_fd = open(file_path, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (file_fd >= 0) { file_writable = 1; } else { file_open_errno = errno; file_fd = open(file_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (file_fd >= 0) file_writable = 0; else file_open_errno = errno; } if (!file_open_logged && file_fd >= 0) { syz_fuse_kmsg2("live_hold_file_open", file_fd, file_writable); file_open_logged = 1; } if (alias_fd < 0) alias_fd = open(alias_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (dir_fd < 0) dir_fd = open(dir_path, O_RDONLY | O_DIRECTORY | O_NONBLOCK | O_CLOEXEC); if (child_fd < 0) child_fd = open(child_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (file_fd >= 0) { ssize_t n = pread(file_fd, tmp, sizeof(tmp), 0); (void)n; } if (alias_fd >= 0) { ssize_t n = pread(alias_fd, tmp, sizeof(tmp), 0); (void)n; } if (child_fd >= 0) { ssize_t n = pread(child_fd, tmp, sizeof(tmp), 0); (void)n; } if ((fault_mode & SYZ_FUSE_SR_CHILD_POLL_LOOP) && file_fd >= 0) { struct pollfd pfd; pfd.fd = file_fd; pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLPRI; pfd.revents = 0; for (int j = 0; j < 4; j++) { int ret = poll(&pfd, 1, 5); (void)ret; syz_fuse_sleep_ms(1); } } if (fault_mode && (i & 3) == 0) { int op_fd = file_fd >= 0 ? file_fd : (alias_fd >= 0 ? alias_fd : child_fd); if (op_fd >= 0) { syz_fuse_kmsg2("live_hold_fileop_fd", op_fd, file_fd >= 0 ? 0 : (alias_fd >= 0 ? 1 : 2)); syz_fuse_store_retrieve_child_op(op_fd, fault_mode, map_len); } } syz_fuse_sleep_ms(10); } if (!file_open_logged) syz_fuse_kmsg2("live_hold_file_open_fail", -1, file_open_errno); if (child_fd >= 0) close(child_fd); if (dir_fd >= 0) close(dir_fd); if (alias_fd >= 0) close(alias_fd); if (file_fd >= 0) close(file_fd); if (root_fd >= 0) close(root_fd); } static void syz_fuse_notify_sink_delayed_mmap_fault_child(const char* mountpoint, uint32_t delay_ms, size_t map_len) { char file_path[512]; char alias_path[512]; char child_path[512]; const char* paths[3]; int fd = -1; int writable = 0; void* map = MAP_FAILED; int prot = PROT_READ; if (!mountpoint) return; if (map_len < 4096) map_len = 4096; if (map_len > 65536) map_len = 65536; syz_fuse_join_path(mountpoint, "file", file_path, sizeof(file_path)); syz_fuse_join_path(mountpoint, "alias", alias_path, sizeof(alias_path)); syz_fuse_join_path2(mountpoint, "dir", "a", child_path, sizeof(child_path)); paths[0] = file_path; paths[1] = alias_path; paths[2] = child_path; for (int i = 0; i < 3 && fd < 0; i++) { fd = open(paths[i], O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { writable = 1; break; } fd = open(paths[i], O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) break; } if (fd < 0) { syz_fuse_kmsg2("nsp_delayed_fault_open_fail", -1, errno); return; } if (writable) prot |= PROT_WRITE; map = mmap(NULL, map_len, prot, MAP_SHARED, fd, 0); syz_fuse_kmsg2("nsp_delayed_fault_map", map == MAP_FAILED ? -1 : 0, map == MAP_FAILED ? errno : writable); close(fd); if (map == MAP_FAILED) return; { volatile char* p = (volatile char*)map; size_t pages = map_len / 4096; if (!pages) pages = 1; for (size_t i = 0; i < pages; i++) { size_t off = (i * 4096) & (map_len - 1); char v = p[off]; if (writable) p[off] = v ^ (char)i; } syz_fuse_kmsg2("nsp_delayed_fault_prefault_done", pages, map_len); } syz_fuse_sleep_ms(delay_ms); syz_fuse_notify_sink_touch_marker(writable, map_len); int rounds = (int)(map_len / 4096) * 4; if (rounds < 32) rounds = 32; if (rounds > 128) rounds = 128; for (int i = 0; i < rounds; i++) { volatile char* p = (volatile char*)map; size_t idx = ((size_t)i * 4096 + ((size_t)i * 131 & 4095)) & (map_len - 1); if (i < 20 || (i & 15) == 0) syz_fuse_kmsg2("nsp_delayed_fault_iter_pre", i, idx); if (map_len >= 49152 && i == 8) { size_t page_off = idx & ~(size_t)4095; char* page = (char*)map + page_off; int ret; syz_fuse_kmsg2("nsp_delayed_fault_target_drop_pre", i, page_off); ret = madvise(page, 4096, MADV_DONTNEED); syz_fuse_kmsg2("nsp_delayed_fault_target_drop_done", ret, ret < 0 ? errno : 0); syz_fuse_edge_memory_pressure( "nsp_delayed_fault_target_pressure", 16); } char v = p[idx]; if (i < 20 || (i & 15) == 0) syz_fuse_kmsg2("nsp_delayed_fault_iter_read", i, idx); if (i == 0) syz_fuse_kmsg2("nsp_delayed_fault_read0", v, map_len); if (writable) { size_t widx = ((size_t)i * 4096 + ((size_t)i * 257 & 4095)) & (map_len - 1); if (i < 20 || (i & 15) == 0) syz_fuse_kmsg2("nsp_delayed_fault_write_pre", i, widx); p[widx] = v ^ (char)i; if (i < 20 || (i & 15) == 0) syz_fuse_kmsg2("nsp_delayed_fault_write_done", i, widx); } if ((i & 15) == 0) syz_fuse_kmsg2("nsp_delayed_fault_progress", i, map_len); if ((i & 7) == 0 && map_len <= 4096) { size_t page_off = idx & ~(size_t)4095; char* page = (char*)map + page_off; syz_fuse_kmsg2("nsp_delayed_fault_pageop", i, page_off); (void)msync(page, 4096, MS_ASYNC); (void)madvise(page, 4096, MADV_DONTNEED); } syz_fuse_sleep_ms(3); } syz_fuse_kmsg2("nsp_delayed_fault_touch", writable, map_len); (void)munmap(map, map_len); } static int syz_fuse_notify_sink_try_own_mount(int fd, char* mount_buf, size_t mount_buf_len, char* buf, int buf_len, struct syz_fuse_req_out* req_out, int wait_ms) { char opts[128]; pid_t server_pid; int status = 0; int mount_ret; int mount_errno; if (fd < 0 || !mount_buf || mount_buf_len < 32) return 0; snprintf(mount_buf, mount_buf_len, "./fusen_nsp_%d_%d", getpid(), fd); mount_buf[mount_buf_len - 1] = 0; (void)mkdir(mount_buf, 0777); snprintf(opts, sizeof(opts), "fd=%d,rootmode=40755,user_id=0,group_id=0", fd); opts[sizeof(opts) - 1] = 0; server_pid = fork(); if (server_pid < 0) return 0; if (server_pid == 0) { mount_ret = mount("none", mount_buf, "fuse", 0, opts); mount_errno = errno; _exit(mount_ret == 0 ? 0 : (mount_errno ? mount_errno : 1)); } for (int i = 0; i < 80; i++) { if (buf && req_out && buf_len >= FUSE_MIN_READ_BUFFER) { int ret = syz_fuse_handle_one_available( fd, buf, buf_len, req_out, 0, wait_ms, "syz_fuse_notify_sink_probe/own_mount_server"); if (ret < 0) { kill(server_pid, SIGKILL); waitpid(server_pid, &status, 0); return 0; } } else { syz_fuse_sleep_ms(wait_ms); } if (waitpid(server_pid, &status, WNOHANG) == server_pid) break; syz_fuse_sleep_ms(1); } if (waitpid(server_pid, &status, WNOHANG) == 0) { kill(server_pid, SIGKILL); waitpid(server_pid, &status, 0); return 0; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { return 0; } return 1; } static volatile long syz_fuse_notify_sink_probe(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { int notify_fd = (int)a0; int read_fd = (int)a1; int file_fd = (int)a2; const char* mountpoint = (const char*)a3; char* buf = (char*)a4; int buf_len = (int)a5; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a6; struct syz_fuse_notify_sink_req* arg = (struct syz_fuse_notify_sink_req*)a7; struct syz_fuse_stateflip_req sf_arg; struct syz_fuse_stateflip_state st; struct syz_fuse_fallback_req_out fallback_req; uint64_t file_nodeid; uint64_t alias_nodeid; uint64_t dir_nodeid; uint64_t child_nodeid; uint64_t child2_nodeid; uint32_t store_size; uint32_t retrieve_size; uint32_t read_len; uint32_t mode; uint32_t direct_nodeids; uint32_t requested_loops; uint32_t loops; uint32_t live_fault_mode; uint64_t store_off; uint64_t retrieve_off; uint64_t prune_nodeids[7]; uint32_t direct_lookup_mask = 0; int wait_ms; int ret = 0; int notify_dup = -1; int clone_drain_fd = -1; int early_prune_done = 0; int early_delete_done = 0; int early_retrieve_done = 0; int early_store_done = 0; int early_second_store_done = 0; int delayed_retrieve_drain = 0; int pre_retrieve_processing = 0; struct syz_fuse_captured_req captured_processing; pid_t materialize_pid = -1; pid_t file_pid = -1; pid_t prelookup_pid = -1; pid_t post_store_pid = -1; pid_t path_storm_pid = -1; pid_t direct_dentry_pid = -1; pid_t direct_read_pid = -1; pid_t delayed_fault_pid = -1; char own_mountpoint[128]; int own_mount = 0; if (!arg || !mountpoint || buf_len < FUSE_MIN_READ_BUFFER) return -1; memset(&captured_processing, 0, sizeof(captured_processing)); syz_fuse_prepare_fallback_req_out(&fallback_req); req_out = &fallback_req.req; file_nodeid = arg->file_nodeid ? arg->file_nodeid : 2; alias_nodeid = arg->alias_nodeid ? arg->alias_nodeid : file_nodeid; dir_nodeid = arg->dir_nodeid ? arg->dir_nodeid : 4; child_nodeid = arg->child_nodeid ? arg->child_nodeid : 5; child2_nodeid = arg->child2_nodeid ? arg->child2_nodeid : 7; store_size = arg->store_size ? arg->store_size : 4096; if (store_size > 1048576) store_size = 1048576; retrieve_size = arg->retrieve_size ? arg->retrieve_size : store_size; if (retrieve_size > 16777216) retrieve_size = 16777216; read_len = arg->read_len ? arg->read_len : FUSE_MIN_READ_BUFFER; mode = arg->mode; direct_nodeids = mode & (SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_DELETE_DIR | SYZ_FUSE_NSP_PRE_RELOOKUP | SYZ_FUSE_NSP_VALID_PRUNE_BATCH); if (direct_nodeids) direct_lookup_mask = syz_fuse_notify_sink_want_lookup_mask(mode); if (direct_nodeids) { file_nodeid = 2; alias_nodeid = 2; dir_nodeid = 4; child_nodeid = 5; child2_nodeid = 7; } if (direct_nodeids) { char* init_payload = fallback_req.init.payload; uint32_t max_write = 4096; uint16_t max_pages = 1; if (mode & (SYZ_FUSE_NSP_STORE_EXTEND | SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE)) { max_write = 65536; max_pages = 16; } if (mode & SYZ_FUSE_NSP_READ_REPLY_RACE) { max_write = 1048576; max_pages = 256; *(uint32_t*)(init_payload + 8) = 1048576; *(uint32_t*)(init_payload + 12) |= 1; syz_fuse_kmsg2("nsp_init_readahead_flags", *(uint32_t*)(init_payload + 8), *(uint32_t*)(init_payload + 12)); } *(uint32_t*)(init_payload + 20) = max_write; *(uint16_t*)(init_payload + 28) = max_pages; syz_fuse_kmsg2("nsp_init_small_maxwrite", max_write, max_pages); } wait_ms = arg->wait_ms; if (wait_ms < 1) wait_ms = 1; if (wait_ms > 500) wait_ms = 500; if (read_fd >= 0 && notify_fd < 0) { own_mount = syz_fuse_notify_sink_try_own_mount( read_fd, own_mountpoint, sizeof(own_mountpoint), buf, buf_len, req_out, wait_ms); if (own_mount) { notify_fd = read_fd; mountpoint = own_mountpoint; } else if (notify_fd < 0) { return -1; } } if (read_fd < 0 || notify_fd < 0) return -1; requested_loops = arg->loops ? arg->loops : 10; loops = requested_loops; if (loops > 32) loops = 32; if (direct_nodeids) { if (wait_ms > 10) wait_ms = 10; if (mode & SYZ_FUSE_NSP_STRESS_LOOPS) { if (loops > 16) loops = 16; } else if (loops > 4) { loops = 4; } } live_fault_mode = 0; if (mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS) live_fault_mode = 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH; store_off = arg->store_offset; if ((mode & SYZ_FUSE_NSP_STRONG_STORE_OFFSET) && store_off < 0x200000) store_off = 0x200000; retrieve_off = arg->retrieve_offset; if ((mode & (SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE)) && (mode & (SYZ_FUSE_NSP_STORE_EXTEND | SYZ_FUSE_NSP_STRONG_STORE_OFFSET))) retrieve_off = store_off; if ((mode & SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE) && notify_fd == read_fd) { notify_dup = dup(notify_fd); if (notify_dup >= 0) notify_fd = notify_dup; } memset(&sf_arg, 0, sizeof(sf_arg)); sf_arg.old_file_nodeid = file_nodeid; sf_arg.new_file_nodeid = arg->new_file_nodeid ? arg->new_file_nodeid : file_nodeid + 10; sf_arg.alias_nodeid = alias_nodeid; sf_arg.dir_nodeid = dir_nodeid; sf_arg.child_nodeid = child_nodeid; sf_arg.child2_nodeid = child2_nodeid; if (direct_nodeids) { sf_arg.subdir_nodeid = 8; sf_arg.subchild_nodeid = 9; } else { sf_arg.subdir_nodeid = arg->subdir_nodeid ? arg->subdir_nodeid : 8; sf_arg.subchild_nodeid = arg->subchild_nodeid ? arg->subchild_nodeid : 9; } sf_arg.store_offset = store_off; sf_arg.retrieve_offset = retrieve_off; sf_arg.store_size = store_size; sf_arg.retrieve_size = retrieve_size; sf_arg.read_len = read_len; sf_arg.mode = SYZ_FUSE_SF_ALIAS_OLD_INODE; if (!(mode & (SYZ_FUSE_NSP_EXPIRE_FILE | SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_DELETE_DIR | SYZ_FUSE_NSP_VALID_PRUNE_BATCH))) sf_arg.mode |= SYZ_FUSE_SF_ZERO_TIMEOUT; sf_arg.wait_ms = wait_ms; sf_arg.loops = loops; memset(&st, 0, sizeof(st)); st.arg = &sf_arg; st.file_name = "file"; st.alias_name = "alias"; st.dir_name = "dir"; st.child_name = "a"; st.child2_name = "b"; st.subdir_name = "sub"; st.subchild_name = "c"; syz_fuse_kmsg2("nsp_preinit_drain_start", wait_ms, loops); for (uint32_t i = 0; i < 16; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_notify_sink_probe/preinit"); if (ret < 0) { syz_fuse_kmsg2("nsp_preinit_drain_error", ret, errno); goto out; } if (ret == 0) break; } syz_fuse_kmsg2("nsp_preinit_drain_done", st.lookup_seen_mask, ret); materialize_pid = fork(); if (materialize_pid == 0) { syz_fuse_stateflip_child_op(mountpoint, sf_arg.mode, loops); _exit(0); } syz_fuse_kmsg2("nsp_materialize_start", direct_lookup_mask ? direct_lookup_mask : SYZ_FUSE_LOOKUP_HIT_FILE, loops); for (uint32_t i = 0; i < loops * (direct_nodeids ? 64 : 8); i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_notify_sink_probe/materialize"); if (ret < 0) { syz_fuse_kmsg2("nsp_materialize_error", ret, errno); goto out; } if (direct_lookup_mask && (st.lookup_seen_mask & direct_lookup_mask) == direct_lookup_mask) { syz_fuse_kmsg2("nsp_materialize_lookup_hit", st.lookup_seen_mask, i); break; } if (!direct_lookup_mask && ret > 0 && i >= loops * 2) break; if (ret > 0) syz_fuse_sleep_ms(1); } syz_fuse_kmsg2("nsp_materialize_done", st.lookup_seen_mask, ret); if ((mode & SYZ_FUSE_NSP_CLONE_DRAIN_FD) && clone_drain_fd < 0) { int oldfd = read_fd; clone_drain_fd = open("/dev/fuse", O_RDWR | O_CLOEXEC); if (clone_drain_fd >= 0) { int clone_ret = ioctl(clone_drain_fd, FUSE_DEV_IOC_CLONE, &oldfd); syz_fuse_kmsg2("nsp_clone_drain_fd", clone_ret, clone_ret < 0 ? errno : clone_drain_fd); if (clone_ret < 0) { (void)close(clone_drain_fd); clone_drain_fd = -1; } } else { syz_fuse_kmsg2("nsp_clone_drain_fd_open", clone_drain_fd, errno); } } /* * Direct-IO READ setup before the dentry/materialization * helpers tended to make the whole notify path collapse * into EIO. Only use this early window for simple * read-only probes; the lifetime/store probe waits until * direct_dentry has warmed the object below. */ if ((mode & SYZ_FUSE_NSP_READ_REPLY_RACE) && direct_read_pid < 0) { char* open_payload = fallback_req.open.payload; *(uint32_t*)(open_payload + 8) = SYZ_FUSE_FOPEN_DIRECT_IO; syz_fuse_kmsg2("nsp_directio_read_prepare_early", clone_drain_fd, loops); direct_read_pid = fork(); if (direct_read_pid == 0) { syz_fuse_notify_sink_direct_read_child( mountpoint, loops + 80, read_len, 10); _exit(0); } if (mode & SYZ_FUSE_NSP_FAULTING_REPLY_RACE) { int early_op; int early_fd = read_fd; memset(&captured_processing, 0, sizeof(captured_processing)); early_op = syz_fuse_notify_sink_read_processing_prefer_read_capture( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 32, "nsp_direct_read_early_prefer_readfd", &captured_processing); if (captured_processing.opcode != FUSE_READ && clone_drain_fd >= 0) { early_fd = clone_drain_fd; early_op = syz_fuse_notify_sink_read_processing_prefer_read_capture( clone_drain_fd, buf, buf_len, req_out, &st, wait_ms, loops * 32, "nsp_direct_read_early_prefer_clone", &captured_processing); } syz_fuse_kmsg2("nsp_direct_read_early_done", early_op, captured_processing.opcode); if (captured_processing.opcode == FUSE_READ) { syz_fuse_kmsg2( "nsp_faulting_reply_candidate", captured_processing.opcode, captured_processing.unique); syz_fuse_faulting_reply_abort_race( early_fd, &captured_processing, wait_ms, requested_loops); } } else { (void)syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 16, "syz_fuse_notify_sink_probe/direct_read_open_early"); if (clone_drain_fd >= 0) (void)syz_fuse_notify_sink_drain( clone_drain_fd, buf, buf_len, req_out, &st, wait_ms, loops * 16, "syz_fuse_notify_sink_probe/direct_read_open_early_clone"); } *(uint32_t*)(open_payload + 8) = (mode & SYZ_FUSE_NSP_READ_REPLY_RACE) ? SYZ_FUSE_FOPEN_DIRECT_IO : 0; } if ((mode & SYZ_FUSE_NSP_FILE_ACTIVITY) && file_fd >= 0) { file_pid = fork(); if (file_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 64 | 0x800 | 0x1000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size > 65536 ? 65536 : store_size); _exit(0); } } else if (mode & SYZ_FUSE_NSP_FILE_ACTIVITY) { syz_fuse_kmsg2("nsp_file_activity_skip", file_fd, mode); } if (mode & SYZ_FUSE_NSP_PRE_RELOOKUP) { prelookup_pid = fork(); if (prelookup_pid == 0) { syz_fuse_stateflip_child_op( mountpoint, SYZ_FUSE_SF_FILE_ACTIVITY, loops + 2); syz_fuse_sleep_ms(200); _exit(0); } if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 6, "syz_fuse_notify_sink_probe/pre_relookup") < 0) goto out; } if (mode & (SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_DELETE_DIR | SYZ_FUSE_NSP_VALID_PRUNE_BATCH)) { if ((mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS) && delayed_fault_pid < 0) { delayed_fault_pid = fork(); if (delayed_fault_pid == 0) { syz_fuse_notify_sink_delayed_mmap_fault_child( mountpoint, 520, store_size); _exit(0); } syz_fuse_kmsg2("nsp_delayed_fault_predirect", delayed_fault_pid, mode); syz_fuse_notify_sink_best_effort_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 12, "syz_fuse_notify_sink_probe/delayed_fault_predirect"); } direct_dentry_pid = fork(); if (direct_dentry_pid == 0) { syz_fuse_notify_sink_live_hold_child(mountpoint, loops + 96, live_fault_mode, store_size); _exit(0); } (void)syz_fuse_notify_sink_drain_until_lookup( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 24, direct_lookup_mask, "syz_fuse_notify_sink_probe/direct_dentry"); (void)syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 4, "syz_fuse_notify_sink_probe/direct_dentry_open"); if ((mode & SYZ_FUSE_NSP_READ_REPLY_RACE) && direct_read_pid < 0) { char* open_payload = fallback_req.open.payload; *(uint32_t*)(open_payload + 8) = SYZ_FUSE_FOPEN_DIRECT_IO; syz_fuse_kmsg2("nsp_directio_read_prepare", clone_drain_fd, loops); direct_read_pid = fork(); if (direct_read_pid == 0) { syz_fuse_notify_sink_direct_read_child( mountpoint, loops + 64, store_size, 260); _exit(0); } (void)syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 12, "syz_fuse_notify_sink_probe/direct_read_open"); if (clone_drain_fd >= 0) (void)syz_fuse_notify_sink_drain( clone_drain_fd, buf, buf_len, req_out, &st, wait_ms, loops * 12, "syz_fuse_notify_sink_probe/direct_read_open_clone"); *(uint32_t*)(open_payload + 8) = (mode & SYZ_FUSE_NSP_READ_REPLY_RACE) ? SYZ_FUSE_FOPEN_DIRECT_IO : 0; } /* * Establish an mmap while the direct dentry/name still * resolves, then let it fault after DELETE/PRUNE/epoch and * connection teardown. Spawning this after epoch made the * child hit EIO on open, so prime the mapping before the * lifetime notifies start and explicitly service its FUSE * requests here. */ if ((mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS) && delayed_fault_pid < 0) { delayed_fault_pid = fork(); if (delayed_fault_pid == 0) { syz_fuse_notify_sink_delayed_mmap_fault_child( mountpoint, 220, store_size); _exit(0); } syz_fuse_kmsg2("nsp_delayed_fault_spawned", delayed_fault_pid, mode); if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 10, "syz_fuse_notify_sink_probe/delayed_fault_prime") < 0) goto out; } if ((mode & SYZ_FUSE_NSP_STORE_BEFORE_LIFETIME) && (mode & SYZ_FUSE_NSP_STORE_EXTEND)) { if (syz_fuse_send_store_msg(notify_fd, file_nodeid, store_off, store_size) == -1) goto out; early_store_done = 1; post_store_pid = fork(); if (post_store_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size > 65536 ? 65536 : store_size); _exit(0); } if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 4, "syz_fuse_notify_sink_probe/pre_lifetime_store") < 0) goto out; if (mode & SYZ_FUSE_NSP_RETRIEVE_FILE) { if (mode & SYZ_FUSE_NSP_DELAY_RETRIEVE_DRAIN) { uint64_t second_off = store_off + 2048; uint32_t second_size = retrieve_size; if (second_size < 4096) second_size = 4096; if (second_size > 65536) second_size = 65536; delayed_retrieve_drain = 1; syz_fuse_kmsg2( "nsp_pre_lifetime_retrieve_defer_send", retrieve_off, retrieve_size); if (mode & SYZ_FUSE_NSP_SECOND_STORE) { if (syz_fuse_send_store_msg( notify_fd, file_nodeid, second_off, second_size) == -1) goto out; early_second_store_done = 1; syz_fuse_kmsg2( "nsp_pre_lifetime_second_store_overlap", second_off, second_size); } } else { if (syz_fuse_send_retrieve_msg( notify_fd, arg->notify_unique, file_nodeid, retrieve_off, retrieve_size) == -1) goto out; early_retrieve_done = 1; if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 4, "syz_fuse_notify_sink_probe/pre_lifetime_retrieve") < 0) goto out; } } } /* * First exercise reverse invalidation without the * child-nodeid check. EXPIRE_ONLY should still call * fuse_invalidate_entry_cache() but leave the dentry * warmer for the following DELETE probe. */ if (mode & SYZ_FUSE_NSP_DELETE_FILE) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.file_name, FUSE_EXPIRE_ONLY_FLAG); if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.alias_name, FUSE_EXPIRE_ONLY_FLAG); if (mode & SYZ_FUSE_NSP_DELETE_DIR) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.dir_name, FUSE_EXPIRE_ONLY_FLAG); /* * The deepest reverse-invalidation path depends on * try_lookup_noperm() finding the exact child dentry in * the parent dcache. Send DELETE while the lookup/open * helper is still keeping the path warm, before PRUNE, * STORE, or other notify writes can disturb that state. */ if (mode & SYZ_FUSE_NSP_DELETE_FILE) (void)syz_fuse_send_delete(notify_fd, 1, file_nodeid, st.file_name); if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) (void)syz_fuse_send_delete(notify_fd, 1, alias_nodeid, st.alias_name); if (mode & SYZ_FUSE_NSP_DELETE_DIR) (void)syz_fuse_send_delete(notify_fd, 1, dir_nodeid, st.dir_name); if (mode & (SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_DELETE_DIR)) { early_delete_done = 1; if (mode & SYZ_FUSE_NSP_DELETE_FILE) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.file_name, 0); if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.alias_name, 0); if (mode & SYZ_FUSE_NSP_DELETE_DIR) (void)syz_fuse_send_inval_entry( notify_fd, 1, st.dir_name, 0); syz_fuse_kmsg2("nsp_after_delete", early_delete_done, mode); syz_fuse_sleep_ms(1); } } if (mode & SYZ_FUSE_NSP_PRUNE_ALIAS) { prune_nodeids[0] = file_nodeid; prune_nodeids[1] = alias_nodeid; prune_nodeids[2] = dir_nodeid; prune_nodeids[3] = child_nodeid; prune_nodeids[4] = child2_nodeid; prune_nodeids[5] = sf_arg.subdir_nodeid; prune_nodeids[6] = sf_arg.subchild_nodeid; if (mode & SYZ_FUSE_NSP_VALID_PRUNE_BATCH) { if (syz_fuse_send_prune_valid_vec( notify_fd, prune_nodeids, sizeof(prune_nodeids) / sizeof(prune_nodeids[0]), requested_loops >= 16 ? 513 : 32) == -1) goto out; } else if (syz_fuse_send_prune_vec(notify_fd, file_nodeid, alias_nodeid, dir_nodeid, child_nodeid) == -1) { goto out; } early_prune_done = 1; syz_fuse_kmsg2("nsp_after_prune", requested_loops, mode); } if ((mode & SYZ_FUSE_NSP_INC_EPOCH) && notify_fd >= 0) { (void)syz_fuse_send_inc_epoch(notify_fd); syz_fuse_kmsg2("nsp_after_early_epoch", requested_loops, mode); syz_fuse_sleep_ms(1); } if ((mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS) && delayed_fault_pid < 0) { delayed_fault_pid = fork(); if (delayed_fault_pid == 0) { syz_fuse_notify_sink_delayed_mmap_fault_child( mountpoint, 180, store_size); _exit(0); } } if (delayed_fault_pid > 0 && (mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS)) { syz_fuse_kmsg2("nsp_prepostclose_fault_window", delayed_fault_pid, mode); if (delayed_retrieve_drain && early_retrieve_done) { syz_fuse_kmsg2("nsp_prepostclose_fault_window_skip", delayed_fault_pid, mode); } else { syz_fuse_notify_sink_best_effort_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops, "syz_fuse_notify_sink_probe/prepostclose_fault_window"); } } if ((mode & SYZ_FUSE_NSP_POST_CLOSE_PRUNE) && direct_dentry_pid > 0 && notify_fd >= 0) { syz_fuse_wait_or_kill(direct_dentry_pid); direct_dentry_pid = -1; if (delayed_retrieve_drain && store_size >= 49152) { syz_fuse_kmsg2("nsp_pre_postclose_abort_delay", store_size, requested_loops); syz_fuse_sleep_ms(128); if (clone_drain_fd >= 0 && !(mode & SYZ_FUSE_NSP_FAULTING_REPLY_RACE)) { syz_fuse_kmsg2( "nsp_clone_preretrieve_processing_read_start", clone_drain_fd, requested_loops); pre_retrieve_processing = syz_fuse_notify_sink_read_processing_no_reply_capture( clone_drain_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops * 2, "nsp_clone_preretrieve_processing_no_reply", &captured_processing); syz_fuse_kmsg2( "nsp_clone_preretrieve_processing_read_done", pre_retrieve_processing, errno); } else if (clone_drain_fd >= 0) { syz_fuse_kmsg2( "nsp_clone_preretrieve_processing_skip_for_read", clone_drain_fd, requested_loops); } if (!early_retrieve_done && (mode & SYZ_FUSE_NSP_RETRIEVE_FILE)) { if (syz_fuse_send_retrieve_msg( notify_fd, arg->notify_unique, file_nodeid, retrieve_off, retrieve_size) == -1) goto out; early_retrieve_done = 1; syz_fuse_kmsg2("nsp_late_retrieve_after_target", retrieve_off, retrieve_size); } { int capture_fd = -1; int fault_ret = 0; uint32_t fault_read_len = read_len; if (clone_drain_fd >= 0 && syz_fuse_wait_readable(clone_drain_fd, wait_ms) >= 0) { capture_fd = clone_drain_fd; } else if ((mode & SYZ_FUSE_NSP_READ_REPLY_RACE) && read_fd >= 0 && read_fd != clone_drain_fd && syz_fuse_wait_readable(read_fd, wait_ms) >= 0) { capture_fd = read_fd; syz_fuse_kmsg2( "nsp_readfd_preabort_ready", read_fd, requested_loops); } if (capture_fd >= 0) { if (fault_read_len < 131072) fault_read_len = 131072; syz_fuse_kmsg2("nsp_clone_preabort_fault_read_pre", fault_read_len, capture_fd); if (mode & SYZ_FUSE_NSP_FAULTING_REPLY_RACE) { syz_fuse_kmsg2( "nsp_directio_read_window_start", capture_fd, direct_read_pid); syz_fuse_sleep_ms(8); fault_ret = 0; } else { fault_ret = syz_fuse_faulting_read( capture_fd, mountpoint, fault_read_len, syz_fuse_notify_sink_fault_mode(mode), wait_ms); } syz_fuse_kmsg2("nsp_clone_preabort_fault_read_done", fault_ret, fault_ret < 0 ? errno : 0); syz_fuse_kmsg2( "nsp_clone_preabort_processing_read_start", capture_fd, requested_loops); if (pre_retrieve_processing > 0 && capture_fd == clone_drain_fd) { fault_ret = pre_retrieve_processing; syz_fuse_kmsg2( "nsp_clone_preabort_processing_read_skip", fault_ret, capture_fd); } else { if (mode & SYZ_FUSE_NSP_READ_REPLY_RACE) fault_ret = syz_fuse_notify_sink_read_processing_prefer_read_capture( capture_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops * 2, "nsp_clone_processing_prefer_read", &captured_processing); else fault_ret = syz_fuse_notify_sink_read_processing_no_reply_capture( capture_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops * 2, "nsp_clone_processing_no_reply", &captured_processing); } if ((mode & SYZ_FUSE_NSP_READ_REPLY_RACE) && captured_processing.opcode != FUSE_READ && read_fd >= 0 && read_fd != capture_fd && syz_fuse_wait_readable(read_fd, 1) >= 0) { struct syz_fuse_captured_req alt_capture; int alt_ret; memset(&alt_capture, 0, sizeof(alt_capture)); alt_ret = syz_fuse_notify_sink_read_processing_prefer_read_capture( read_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops * 2, "nsp_readfd_processing_prefer_read", &alt_capture); syz_fuse_kmsg2("nsp_readfd_processing_done", alt_ret, alt_capture.opcode); if (alt_capture.opcode == FUSE_READ || (captured_processing.opcode == 0 && alt_capture.opcode != 0)) { captured_processing = alt_capture; capture_fd = read_fd; } } syz_fuse_kmsg2( "nsp_clone_preabort_processing_read_done", fault_ret, errno); syz_fuse_kmsg2("nsp_clone_preabort_keep_processing", capture_fd, fault_ret); if ((mode & SYZ_FUSE_NSP_FAULTING_REPLY_RACE) && (captured_processing.opcode == FUSE_READ || captured_processing.opcode == FUSE_WRITE)) { syz_fuse_kmsg2( "nsp_faulting_reply_candidate", captured_processing.opcode, captured_processing.unique); syz_fuse_faulting_reply_abort_race( capture_fd, &captured_processing, wait_ms, requested_loops); } } } if ((mode & SYZ_FUSE_NSP_CLOSE_CLONE_BEFORE_PRUNE) && clone_drain_fd >= 0) { syz_fuse_kmsg2( "nsp_clone_release_before_prune", clone_drain_fd, captured_processing.opcode ? captured_processing.opcode : pre_retrieve_processing); (void)close(clone_drain_fd); clone_drain_fd = -1; syz_fuse_edge_memory_pressure( "nsp_after_clone_release_pressure", requested_loops); syz_fuse_sleep_ms(1); } prune_nodeids[0] = file_nodeid; prune_nodeids[1] = alias_nodeid; prune_nodeids[2] = dir_nodeid; prune_nodeids[3] = child_nodeid; prune_nodeids[4] = child2_nodeid; prune_nodeids[5] = sf_arg.subdir_nodeid; prune_nodeids[6] = sf_arg.subchild_nodeid; if (mode & SYZ_FUSE_NSP_VALID_PRUNE_BATCH) { if (syz_fuse_send_prune_valid_vec( notify_fd, prune_nodeids, sizeof(prune_nodeids) / sizeof(prune_nodeids[0]), requested_loops >= 16 ? 513 : 32) == -1) goto out; } else if (syz_fuse_send_prune_vec(notify_fd, file_nodeid, alias_nodeid, dir_nodeid, child_nodeid) == -1) { goto out; } syz_fuse_kmsg2("nsp_after_postclose_prune", requested_loops, mode); if (mode & SYZ_FUSE_NSP_UMOUNT_AFTER) { syz_fuse_abort_all_connections_now( "nsp_postclose_internal_abort"); if (store_size >= 49152) syz_fuse_kmsg2( "nsp_postclose_internal_pressure_defer", store_size, requested_loops); else syz_fuse_edge_memory_pressure( "nsp_postclose_internal_pressure", requested_loops); } if (delayed_fault_pid > 0 && (mode & SYZ_FUSE_NSP_LIVE_HOLD_FAULTS)) { int postclose_read_fd = clone_drain_fd >= 0 ? clone_drain_fd : read_fd; if (delayed_retrieve_drain) syz_fuse_kmsg2("nsp_delayed_retrieve_drain_window", delayed_fault_pid, mode); if (clone_drain_fd >= 0) syz_fuse_kmsg2("nsp_clone_drain_use", clone_drain_fd, delayed_fault_pid); syz_fuse_kmsg2("nsp_postclose_fault_window", delayed_fault_pid, mode); if (syz_fuse_notify_sink_drain_child_window( delayed_fault_pid, postclose_read_fd, buf, buf_len, req_out, &st, wait_ms, requested_loops * 24, "syz_fuse_notify_sink_probe/postclose_fault_window")) delayed_fault_pid = -1; if (clone_drain_fd >= 0) { syz_fuse_kmsg2("nsp_clone_drain_close", clone_drain_fd, delayed_fault_pid); (void)close(clone_drain_fd); clone_drain_fd = -1; } } syz_fuse_sleep_ms(1); } } if (!early_store_done && (mode & SYZ_FUSE_NSP_STORE_EXTEND) && syz_fuse_send_store_msg(notify_fd, file_nodeid, store_off, store_size) == -1) goto out; if (!early_store_done && (mode & SYZ_FUSE_NSP_STORE_EXTEND)) { post_store_pid = fork(); if (post_store_pid == 0) { syz_fuse_store_retrieve_child_op( file_fd, 1 | 2 | 4 | 8 | 16 | 64 | 0x800 | 0x1000 | 0x2000 | SYZ_FUSE_SR_CHILD_POST_TEARDOWN_TOUCH, store_size > 65536 ? 65536 : store_size); _exit(0); } if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 4, "syz_fuse_notify_sink_probe/post_store") < 0) goto out; } if ((mode & SYZ_FUSE_NSP_SECOND_STORE) && !early_second_store_done && syz_fuse_send_store_msg(notify_fd, file_nodeid, store_off + 4096, retrieve_size) == -1) goto out; if ((mode & SYZ_FUSE_NSP_STORE_EXTEND) && (mode & (SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE)) && (mode & (SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE | SYZ_FUSE_NSP_UMOUNT_AFTER | SYZ_FUSE_NSP_FAULT_MPROTECT | SYZ_FUSE_NSP_FAULT_MUNMAP | SYZ_FUSE_NSP_READV_SPLIT))) { uint64_t retrieve_nodeid = file_nodeid; if (mode & SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE) retrieve_nodeid = alias_nodeid; if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, retrieve_nodeid, retrieve_off, retrieve_size) == -1) goto out; early_retrieve_done = 1; if (mode & SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE) { (void)close(notify_fd); if (notify_fd == notify_dup) notify_dup = -1; notify_fd = -1; } if (mode & SYZ_FUSE_NSP_UMOUNT_AFTER) (void)umount2(mountpoint, MNT_DETACH); if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_notify_sink_fault_mode(mode), wait_ms); if (notify_fd < 0 || (mode & SYZ_FUSE_NSP_UMOUNT_AFTER)) goto out; } if (!early_delete_done && (mode & SYZ_FUSE_NSP_DELETE_FILE)) (void)syz_fuse_send_delete(notify_fd, 1, file_nodeid, st.file_name); if (!early_delete_done && (mode & SYZ_FUSE_NSP_DELETE_ALIAS)) (void)syz_fuse_send_delete(notify_fd, 1, alias_nodeid, st.alias_name); if (!early_delete_done && (mode & SYZ_FUSE_NSP_DELETE_DIR)) (void)syz_fuse_send_delete(notify_fd, 1, dir_nodeid, st.dir_name); if ((mode & SYZ_FUSE_NSP_EXPIRE_FILE)) (void)syz_fuse_send_inval_entry(notify_fd, 1, st.file_name, FUSE_EXPIRE_ONLY_FLAG); if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) (void)syz_fuse_send_inval_entry(notify_fd, 1, st.alias_name, FUSE_EXPIRE_ONLY_FLAG); if (mode & SYZ_FUSE_NSP_INC_EPOCH) (void)syz_fuse_send_inc_epoch(notify_fd); if (mode & SYZ_FUSE_NSP_DELETE_FILE) (void)syz_fuse_send_inval_entry(notify_fd, 1, st.file_name, 0); if (mode & SYZ_FUSE_NSP_DELETE_ALIAS) (void)syz_fuse_send_inval_entry(notify_fd, 1, st.alias_name, 0); if (mode & SYZ_FUSE_NSP_DELETE_DIR) (void)syz_fuse_send_inval_entry(notify_fd, 1, st.dir_name, 0); if ((mode & SYZ_FUSE_NSP_PRUNE_ALIAS) && !early_prune_done) { if (mode & SYZ_FUSE_NSP_PRE_RELOOKUP) { path_storm_pid = fork(); if (path_storm_pid == 0) { syz_fuse_stateflip_child_op( mountpoint, SYZ_FUSE_SF_FILE_ACTIVITY, loops + 2); _exit(0); } if (syz_fuse_notify_sink_drain( read_fd, buf, buf_len, req_out, &st, wait_ms, loops * 4, "syz_fuse_notify_sink_probe/path_storm") < 0) goto out; } if (mode & SYZ_FUSE_NSP_VALID_PRUNE_BATCH) { prune_nodeids[0] = file_nodeid; prune_nodeids[1] = alias_nodeid; prune_nodeids[2] = dir_nodeid; prune_nodeids[3] = child_nodeid; prune_nodeids[4] = child2_nodeid; prune_nodeids[5] = sf_arg.subdir_nodeid; prune_nodeids[6] = sf_arg.subchild_nodeid; if (syz_fuse_send_prune_valid_vec( notify_fd, prune_nodeids, sizeof(prune_nodeids) / sizeof(prune_nodeids[0]), requested_loops >= 16 ? 513 : 32) == -1) goto out; } else if (syz_fuse_send_prune_vec(notify_fd, file_nodeid, alias_nodeid, dir_nodeid, child_nodeid) == -1) { goto out; } if ((mode & SYZ_FUSE_NSP_VALID_PRUNE_BATCH) && direct_dentry_pid > 0) { syz_fuse_wait_or_kill(direct_dentry_pid); direct_dentry_pid = -1; prune_nodeids[0] = file_nodeid; prune_nodeids[1] = alias_nodeid; prune_nodeids[2] = dir_nodeid; prune_nodeids[3] = child_nodeid; prune_nodeids[4] = child2_nodeid; prune_nodeids[5] = sf_arg.subdir_nodeid; prune_nodeids[6] = sf_arg.subchild_nodeid; if (syz_fuse_send_prune_valid_vec( notify_fd, prune_nodeids, sizeof(prune_nodeids) / sizeof(prune_nodeids[0]), loops >= 16 ? 513 : 32) == -1) goto out; } } if (mode & SYZ_FUSE_NSP_REVALIDATE_AFTER) { pid_t reval_pid = fork(); if (reval_pid == 0) { syz_fuse_stateflip_child_op( mountpoint, SYZ_FUSE_SF_RENAME_STORM | SYZ_FUSE_SF_UNLINK_STORM | SYZ_FUSE_SF_FILE_ACTIVITY, loops / 2 + 1); _exit(0); } for (uint32_t i = 0; i < loops * 4; i++) { ret = syz_fuse_handle_one_available_stateflip( read_fd, buf, buf_len, req_out, &st, wait_ms, "syz_fuse_notify_sink_probe/revalidate"); if (ret < 0) { if (reval_pid > 0) syz_fuse_wait_or_kill(reval_pid); goto out; } if (ret > 0 && i >= loops) break; } if (reval_pid > 0) syz_fuse_wait_or_kill(reval_pid); } if ((mode & (SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE)) && !early_retrieve_done) { uint64_t retrieve_nodeid = file_nodeid; if (mode & SYZ_FUSE_NSP_RETRIEVE_OLD_AFTER_PRUNE) retrieve_nodeid = alias_nodeid; if (syz_fuse_send_retrieve_msg(notify_fd, arg->notify_unique, retrieve_nodeid, retrieve_off, retrieve_size) == -1) goto out; if (mode & SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE) { (void)close(notify_fd); if (notify_fd == notify_dup) notify_dup = -1; notify_fd = -1; } if (mode & SYZ_FUSE_NSP_UMOUNT_AFTER) (void)umount2(mountpoint, MNT_DETACH); if (syz_fuse_wait_readable(read_fd, wait_ms) >= 0) ret = syz_fuse_faulting_read( read_fd, mountpoint, read_len, syz_fuse_notify_sink_fault_mode(mode), wait_ms); } out: if (mode & SYZ_FUSE_NSP_UMOUNT_AFTER) (void)umount2(mountpoint, MNT_DETACH); if (own_mount) (void)umount2(own_mountpoint, MNT_DETACH); if (clone_drain_fd >= 0) (void)close(clone_drain_fd); if (notify_dup >= 0) (void)close(notify_dup); if (materialize_pid > 0) syz_fuse_wait_or_kill(materialize_pid); if (file_pid > 0) syz_fuse_wait_or_kill(file_pid); if (prelookup_pid > 0) syz_fuse_wait_or_kill(prelookup_pid); if (post_store_pid > 0) syz_fuse_wait_or_kill(post_store_pid); if (path_storm_pid > 0) syz_fuse_wait_or_kill(path_storm_pid); if (direct_dentry_pid > 0) syz_fuse_wait_or_kill(direct_dentry_pid); if (direct_read_pid > 0) syz_fuse_wait_or_kill(direct_read_pid); if (delayed_fault_pid > 0) syz_fuse_wait_or_kill(delayed_fault_pid); return ret; } static void syz_fuse_notify_sink_fixed_arg( struct syz_fuse_notify_sink_req* arg, uint64_t unique, uint32_t mode, uint64_t store_off, uint32_t store_size, uint32_t retrieve_size, uint32_t loops) { memset(arg, 0, sizeof(*arg)); arg->notify_unique = unique; arg->file_nodeid = 2; arg->new_file_nodeid = 12; arg->alias_nodeid = 2; arg->dir_nodeid = 4; arg->child_nodeid = 5; arg->child2_nodeid = 7; arg->subdir_nodeid = 8; arg->subchild_nodeid = 9; arg->store_offset = store_off; arg->retrieve_offset = store_off; arg->store_size = store_size; arg->retrieve_size = retrieve_size; arg->read_len = FUSE_MIN_READ_BUFFER; arg->mode = mode; arg->wait_ms = 25; arg->loops = loops; } static volatile long syz_fuse_notify_sink_fixed_run( volatile long a0, volatile long a1, volatile long a2, volatile long a3, struct syz_fuse_notify_sink_req* arg) { int fd = (int)a0; const char* mountpoint = (const char*)a1; char* buf = (char*)a2; int buf_len = (int)a3; if (!arg || fd < 0 || !mountpoint || buf_len < FUSE_MIN_READ_BUFFER) return -1; if (!buf || buf == MAP_FAILED) return -1; return syz_fuse_notify_sink_probe(fd, fd, -1, (long)mountpoint, (long)buf, buf_len, 0, (long)arg); } static volatile long syz_fuse_notify_delete_alias_probe( volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_notify_sink_req arg; (void)a0; (void)a1; (void)a2; (void)a3; syz_fuse_notify_sink_fixed_arg( &arg, 0x7101, SYZ_FUSE_NSP_EXPIRE_FILE | SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_FILE_ACTIVITY | SYZ_FUSE_NSP_REVALIDATE_AFTER | SYZ_FUSE_NSP_PRE_RELOOKUP, 0, 4096, 4096, 6); return syz_fuse_notify_sink_fixed_run(a0, a1, a2, a3, &arg); } static volatile long syz_fuse_notify_prune_alias_probe( volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_notify_sink_req arg; (void)a0; (void)a1; (void)a2; (void)a3; syz_fuse_notify_sink_fixed_arg( &arg, 0x7102, SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_FILE_ACTIVITY | SYZ_FUSE_NSP_PRE_RELOOKUP | SYZ_FUSE_NSP_VALID_PRUNE_BATCH, 0, 4096, 4096, 16); return syz_fuse_notify_sink_fixed_run(a0, a1, a2, a3, &arg); } static volatile long syz_fuse_notify_prune_delete_probe( volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_notify_sink_req arg; (void)a0; (void)a1; (void)a2; (void)a3; syz_fuse_notify_sink_fixed_arg( &arg, 0x7103, SYZ_FUSE_NSP_EXPIRE_FILE | SYZ_FUSE_NSP_DELETE_FILE | SYZ_FUSE_NSP_DELETE_ALIAS | SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_INC_EPOCH | SYZ_FUSE_NSP_STORE_EXTEND | SYZ_FUSE_NSP_SECOND_STORE | SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_DELAY_RETRIEVE_DRAIN | SYZ_FUSE_NSP_CLONE_DRAIN_FD | SYZ_FUSE_NSP_CLOSE_CLONE_BEFORE_PRUNE | SYZ_FUSE_NSP_FAULTING_REPLY_RACE | SYZ_FUSE_NSP_STORE_BEFORE_LIFETIME | SYZ_FUSE_NSP_FILE_ACTIVITY | SYZ_FUSE_NSP_PRE_RELOOKUP | SYZ_FUSE_NSP_VALID_PRUNE_BATCH | SYZ_FUSE_NSP_REVALIDATE_AFTER | SYZ_FUSE_NSP_POST_CLOSE_PRUNE | SYZ_FUSE_NSP_LIVE_HOLD_FAULTS | SYZ_FUSE_NSP_UMOUNT_AFTER | SYZ_FUSE_NSP_FAULT_MPROTECT | SYZ_FUSE_NSP_FAULT_MUNMAP | SYZ_FUSE_NSP_READV_SPLIT, 32768, 65536, 65536, 16); arg.read_len = 1048576; return syz_fuse_notify_sink_fixed_run(a0, a1, a2, a3, &arg); } static volatile long syz_fuse_notify_read_reply_probe( volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_notify_sink_req arg; (void)a0; (void)a1; (void)a2; (void)a3; syz_fuse_notify_sink_fixed_arg( &arg, 0x7105, SYZ_FUSE_NSP_PRUNE_ALIAS | SYZ_FUSE_NSP_STORE_EXTEND | SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_DELAY_RETRIEVE_DRAIN | SYZ_FUSE_NSP_CLONE_DRAIN_FD | SYZ_FUSE_NSP_CLOSE_CLONE_BEFORE_PRUNE | SYZ_FUSE_NSP_FAULTING_REPLY_RACE | SYZ_FUSE_NSP_READ_REPLY_RACE | SYZ_FUSE_NSP_STORE_BEFORE_LIFETIME | SYZ_FUSE_NSP_FILE_ACTIVITY | SYZ_FUSE_NSP_PRE_RELOOKUP | SYZ_FUSE_NSP_VALID_PRUNE_BATCH | SYZ_FUSE_NSP_POST_CLOSE_PRUNE | SYZ_FUSE_NSP_LIVE_HOLD_FAULTS | SYZ_FUSE_NSP_FAULT_MPROTECT | SYZ_FUSE_NSP_FAULT_MUNMAP | SYZ_FUSE_NSP_READV_SPLIT, 32768, 65536, 65536, 16); arg.read_len = 1048576; return syz_fuse_notify_sink_fixed_run(a0, a1, a2, a3, &arg); } static volatile long syz_fuse_notify_store_retrieve_probe( volatile long a0, volatile long a1, volatile long a2, volatile long a3) { struct syz_fuse_notify_sink_req arg; (void)a0; (void)a1; (void)a2; (void)a3; syz_fuse_notify_sink_fixed_arg( &arg, 0x7104, SYZ_FUSE_NSP_STORE_EXTEND | SYZ_FUSE_NSP_RETRIEVE_FILE | SYZ_FUSE_NSP_FILE_ACTIVITY | SYZ_FUSE_NSP_FAULT_MPROTECT | SYZ_FUSE_NSP_UMOUNT_AFTER | SYZ_FUSE_NSP_CLOSE_NOTIFY_AFTER_RETRIEVE | SYZ_FUSE_NSP_STRONG_STORE_OFFSET, 0x200000, 4096, 4097, 4); return syz_fuse_notify_sink_fixed_run(a0, a1, a2, a3, &arg); } static volatile long syz_fuse_retrieve_notify(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5) { int notify_fd = (int)a0; int read_fd = (int)a1; char* buf = (char*)a2; int buf_len = (int)a3; struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a4; struct syz_fuse_retrieve_req* retrieve = (struct syz_fuse_retrieve_req*)a5; int wait_timeout = 300; if (!req_out || !retrieve) { return -1; } if (buf_len < FUSE_MIN_READ_BUFFER) { return -1; } struct fuse_notify_retrieve_msg notify; memset(¬ify, 0, sizeof(notify)); notify.len = sizeof(notify); notify.code = FUSE_NOTIFY_RETRIEVE_CODE; notify.notify_unique = retrieve->notify_unique; notify.nodeid = retrieve->nodeid; notify.offset = retrieve->offset; notify.size = retrieve->size; if (write(notify_fd, ¬ify, sizeof(notify)) == -1) { return -1; } if ((retrieve->mode & 1) && notify_fd != read_fd) close(notify_fd); if (retrieve->mode & 2) syz_fuse_sleep_ms(10); if (syz_fuse_wait_readable(read_fd, wait_timeout) < 0) { return -1; } int ret = read(read_fd, buf, buf_len); if (ret == -1) { return -1; } if ((size_t)ret < sizeof(struct fuse_in_header)) { return -1; } const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; if (in_hdr->len > (uint32_t)ret) { return -1; } if (in_hdr->opcode == FUSE_NOTIFY_REPLY) return 0; struct fuse_out_header* out_hdr = NULL; int pick = syz_fuse_pick_response(req_out, in_hdr->opcode, &out_hdr, "syz_fuse_retrieve_notify"); if (pick < 0) return -1; if (pick == 0 && fuse_send_response(read_fd, in_hdr, out_hdr) == -1) return -1; return 0; } struct thread_t { int created, call; event_t ready, done; }; static struct thread_t threads[16]; static void execute_call(int call); static int running; static void* thr(void* arg) { struct thread_t* th = (struct thread_t*)arg; for (;;) { event_wait(&th->ready); event_reset(&th->ready); execute_call(th->call); __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); event_set(&th->done); } return 0; } static void execute_one(void) { if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { } int i, call, thread; for (call = 0; call < 2; call++) { for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); thread++) { struct thread_t* th = &threads[thread]; if (!th->created) { th->created = 1; event_init(&th->ready); event_init(&th->done); event_set(&th->done); thread_start(thr, th); } if (!event_isset(&th->done)) continue; event_reset(&th->done); th->call = call; __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); event_set(&th->ready); event_timedwait(&th->done, 50 + (call == 1 ? 2800 : 0)); break; } } for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) sleep_ms(1); } static void execute_one(void); #define WAIT_FLAGS __WALL static void loop(void) { int iter = 0; for (; iter < 300; iter++) { int pid = fork(); if (pid < 0) exit(1); if (pid == 0) { setup_test(); execute_one(); exit(0); } int status = 0; uint64_t start = current_time_ms(); for (;;) { sleep_ms(10); if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) break; if (current_time_ms() - start < 5000) continue; kill_and_wait(pid, &status); break; } } } uint64_t r[1] = {0xffffffffffffffff}; void execute_call(int call) { intptr_t res = 0; switch (call) { case 0: // openat$fuse arguments: [ // fd: const = 0xffffffffffffff9c (8 bytes) // file: ptr[in, buffer] { // buffer: {2f 64 65 76 2f 66 75 73 65 00} (length 0xa) // } // flags: const = 0x2 (4 bytes) // mode: const = 0x0 (2 bytes) // ] // returns fd_fuse memcpy((void*)0x200000000000, "/dev/fuse\000", 10); res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000000ul, /*flags=*/2, /*mode=*/0); if (res != -1) r[0] = res; break; case 1: // syz_fuse_notify_sink_probe arguments: [ // notifyfd: fd_fuse (resource) // readfd: fd_fuse (resource) // file: fd (resource) // mountpoint: ptr[in, buffer] { // buffer: {2e 2f 66 75 73 65 6d 6e 74 00} (length 0xa) // } // buf: ptr[in, buffer] { // buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x2000) // } // len: bytesize = 0x120000 (8 bytes) // res: intptr = 0x0 (8 bytes) // arg: ptr[in, syz_fuse_notify_sink_req] { // syz_fuse_notify_sink_req { // notify_unique: int64 = 0x7105 (8 bytes) // file_nodeid: int64 = 0x2 (8 bytes) // new_file_nodeid: int64 = 0xc (8 bytes) // alias_nodeid: int64 = 0x2 (8 bytes) // dir_nodeid: int64 = 0x4 (8 bytes) // child_nodeid: int64 = 0x5 (8 bytes) // child2_nodeid: int64 = 0x7 (8 bytes) // subdir_nodeid: int64 = 0x8 (8 bytes) // subchild_nodeid: int64 = 0x9 (8 bytes) // store_offset: int64 = 0x8000 (8 bytes) // retrieve_offset: int64 = 0x8000 (8 bytes) // store_size: int32 = 0x10000 (4 bytes) // retrieve_size: int32 = 0x10000 (4 bytes) // read_len: int32 = 0x100000 (4 bytes) // mode: int32 = 0x18020000 (4 bytes) // wait_ms: int32 = 0x0 (4 bytes) // loops: int32 = 0x20 (4 bytes) // } // } // ] memcpy((void*)0x200000000100, "./fusemnt\000", 10); memset((void*)0x200000200000, 0, 8192); *(uint64_t*)0x200000032000 = 0x7105; *(uint64_t*)0x200000032008 = 2; *(uint64_t*)0x200000032010 = 0xc; *(uint64_t*)0x200000032018 = 2; *(uint64_t*)0x200000032020 = 4; *(uint64_t*)0x200000032028 = 5; *(uint64_t*)0x200000032030 = 7; *(uint64_t*)0x200000032038 = 8; *(uint64_t*)0x200000032040 = 9; *(uint64_t*)0x200000032048 = 0x8000; *(uint64_t*)0x200000032050 = 0x8000; *(uint32_t*)0x200000032058 = 0x10000; *(uint32_t*)0x20000003205c = 0x10000; *(uint32_t*)0x200000032060 = 0x100000; *(uint32_t*)0x200000032064 = 0x18020000; *(uint32_t*)0x200000032068 = 0; *(uint32_t*)0x20000003206c = 0x20; syz_fuse_notify_sink_probe(/*notifyfd=*/-1, /*readfd=*/r[0], /*file=*/-1, /*mountpoint=*/0x200000000100, /*buf=*/0x200000200000, /*len=*/0x120000, /*res=*/0, /*arg=*/0x200000032000); break; } } int main(void) { syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul); syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul); syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul); const char* reason; (void)reason; do_sandbox_none(); return 0; }