text: // WARNING in efs_kill_sb
  // https://syzkaller.appspot.com/bug?id=9df050644b79c1f74ad6
  // status:0
  // autogenerated by syzkaller (https://github.com/google/syzkaller)
  
  #define _GNU_SOURCE
  
  #include <dirent.h>
  #include <endian.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <sched.h>
  #include <signal.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/ioctl.h>
  #include <sys/mount.h>
  #include <sys/prctl.h>
  #include <sys/resource.h>
  #include <sys/stat.h>
  #include <sys/syscall.h>
  #include <sys/time.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  #include <time.h>
  #include <unistd.h>
  
  #include <linux/capability.h>
  #include <linux/loop.h>
  
  static unsigned long long procid;
  
  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 use_temporary_dir(void)
  {
    char tmpdir_template[] = "./syzkaller.XXXXXX";
    char* tmpdir = mkdtemp(tmpdir_template);
    if (!tmpdir)
      exit(1);
    if (chmod(tmpdir, 0777))
      exit(1);
    if (chdir(tmpdir))
      exit(1);
  }
  
  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;
  }
  
  #define MAX_FDS 30
  
  struct fs_image_segment {
    void* data;
    uintptr_t size;
    uintptr_t offset;
  };
  
  #define IMAGE_MAX_SEGMENTS 4096
  #define IMAGE_MAX_SIZE (129 << 20)
  
  #define sys_memfd_create 319
  
  static unsigned long fs_image_segment_check(unsigned long size,
                                              unsigned long nsegs,
                                              struct fs_image_segment* segs)
  {
    if (nsegs > IMAGE_MAX_SEGMENTS)
      nsegs = IMAGE_MAX_SEGMENTS;
    for (size_t i = 0; i < nsegs; i++) {
      if (segs[i].size > IMAGE_MAX_SIZE)
        segs[i].size = IMAGE_MAX_SIZE;
      segs[i].offset %= IMAGE_MAX_SIZE;
      if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size)
        segs[i].offset = IMAGE_MAX_SIZE - segs[i].size;
      if (size < segs[i].offset + segs[i].offset)
        size = segs[i].offset + segs[i].offset;
    }
    if (size > IMAGE_MAX_SIZE)
      size = IMAGE_MAX_SIZE;
    return size;
  }
  static int setup_loop_device(long unsigned size, long unsigned nsegs,
                               struct fs_image_segment* segs,
                               const char* loopname, int* memfd_p, int* loopfd_p)
  {
    int err = 0, loopfd = -1;
    size = fs_image_segment_check(size, nsegs, segs);
    int memfd = syscall(sys_memfd_create, "syzkaller", 0);
    if (memfd == -1) {
      err = errno;
      goto error;
    }
    if (ftruncate(memfd, size)) {
      err = errno;
      goto error_close_memfd;
    }
    for (size_t i = 0; i < nsegs; i++) {
      if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
      }
    }
    loopfd = open(loopname, O_RDWR);
    if (loopfd == -1) {
      err = errno;
      goto error_close_memfd;
    }
    if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
      if (errno != EBUSY) {
        err = errno;
        goto error_close_loop;
      }
      ioctl(loopfd, LOOP_CLR_FD, 0);
      usleep(1000);
      if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
        err = errno;
        goto error_close_loop;
      }
    }
    *memfd_p = memfd;
    *loopfd_p = loopfd;
    return 0;
  
  error_close_loop:
    close(loopfd);
  error_close_memfd:
    close(memfd);
  error:
    errno = err;
    return -1;
  }
  
  static long syz_mount_image(volatile long fsarg, volatile long dir,
                              volatile unsigned long size,
                              volatile unsigned long nsegs,
                              volatile long segments, volatile long flags,
                              volatile long optsarg)
  {
    struct fs_image_segment* segs = (struct fs_image_segment*)segments;
    int res = -1, err = 0, loopfd = -1, memfd = -1, need_loop_device = !!segs;
    char* mount_opts = (char*)optsarg;
    char* target = (char*)dir;
    char* fs = (char*)fsarg;
    char* source = NULL;
    char loopname[64];
    if (need_loop_device) {
      memset(loopname, 0, sizeof(loopname));
      snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
      if (setup_loop_device(size, nsegs, segs, loopname, &memfd, &loopfd) == -1)
        return -1;
      source = loopname;
    }
    mkdir(target, 0777);
    char opts[256];
    memset(opts, 0, sizeof(opts));
    if (strlen(mount_opts) > (sizeof(opts) - 32)) {
    }
    strncpy(opts, mount_opts, sizeof(opts) - 32);
    if (strcmp(fs, "iso9660") == 0) {
      flags |= MS_RDONLY;
    } else if (strncmp(fs, "ext", 3) == 0) {
      if (strstr(opts, "errors=panic") || strstr(opts, "errors=remount-ro") == 0)
        strcat(opts, ",errors=continue");
    } else if (strcmp(fs, "xfs") == 0) {
      strcat(opts, ",nouuid");
    }
    res = mount(source, target, fs, flags, opts);
    if (res == -1) {
      err = errno;
      goto error_clear_loop;
    }
    res = open(target, O_RDONLY | O_DIRECTORY);
    if (res == -1) {
      err = errno;
    }
  
  error_clear_loop:
    if (need_loop_device) {
      ioctl(loopfd, LOOP_CLR_FD, 0);
      close(loopfd);
      close(memfd);
    }
    errno = err;
    return res;
  }
  
  static void setup_common()
  {
    if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) {
    }
  }
  
  static void loop();
  
  static void sandbox_common()
  {
    prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
    setsid();
    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 = 0;
    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);
    setup_common();
    sandbox_common();
    drop_caps();
    if (unshare(CLONE_NEWNET)) {
    }
    loop();
    exit(1);
  }
  
  #define FS_IOC_SETFLAGS _IOW('f', 2, long)
  static void remove_dir(const char* dir)
  {
    int iter = 0;
    DIR* dp = 0;
  retry:
    while (umount2(dir, MNT_DETACH) == 0) {
    }
    dp = opendir(dir);
    if (dp == NULL) {
      if (errno == EMFILE) {
        exit(1);
      }
      exit(1);
    }
    struct dirent* ep = 0;
    while ((ep = readdir(dp))) {
      if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
        continue;
      char filename[FILENAME_MAX];
      snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
      while (umount2(filename, MNT_DETACH) == 0) {
      }
      struct stat st;
      if (lstat(filename, &st))
        exit(1);
      if (S_ISDIR(st.st_mode)) {
        remove_dir(filename);
        continue;
      }
      int i;
      for (i = 0;; i++) {
        if (unlink(filename) == 0)
          break;
        if (errno == EPERM) {
          int fd = open(filename, O_RDONLY);
          if (fd != -1) {
            long flags = 0;
            if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) {
            }
            close(fd);
            continue;
          }
        }
        if (errno == EROFS) {
          break;
        }
        if (errno != EBUSY || i > 100)
          exit(1);
        if (umount2(filename, MNT_DETACH))
          exit(1);
      }
    }
    closedir(dp);
    for (int i = 0;; i++) {
      if (rmdir(dir) == 0)
        break;
      if (i < 100) {
        if (errno == EPERM) {
          int fd = open(dir, O_RDONLY);
          if (fd != -1) {
            long flags = 0;
            if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) {
            }
            close(fd);
            continue;
          }
        }
        if (errno == EROFS) {
          break;
        }
        if (errno == EBUSY) {
          if (umount2(dir, MNT_DETACH))
            exit(1);
          continue;
        }
        if (errno == ENOTEMPTY) {
          if (iter < 100) {
            iter++;
            goto retry;
          }
        }
      }
      exit(1);
    }
  }
  
  static int inject_fault(int nth)
  {
    int fd;
    fd = open("/proc/thread-self/fail-nth", O_RDWR);
    if (fd == -1)
      exit(1);
    char buf[16];
    sprintf(buf, "%d", nth + 1);
    if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
      exit(1);
    return fd;
  }
  
  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 reset_loop()
  {
    char buf[64];
    snprintf(buf, sizeof(buf), "/dev/loop%llu", procid);
    int loopfd = open(buf, O_RDWR);
    if (loopfd != -1) {
      ioctl(loopfd, LOOP_CLR_FD, 0);
      close(loopfd);
    }
  }
  
  static void setup_test()
  {
    prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
    setpgrp();
    write_file("/proc/self/oom_score_adj", "1000");
  }
  
  static void close_fds()
  {
    for (int fd = 3; fd < MAX_FDS; fd++)
      close(fd);
  }
  
  static void setup_fault()
  {
    static struct {
      const char* file;
      const char* val;
      bool fatal;
    } files[] = {
        {"/sys/kernel/debug/failslab/ignore-gfp-wait", "N", true},
        {"/sys/kernel/debug/fail_futex/ignore-private", "N", false},
        {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N", false},
        {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N", false},
        {"/sys/kernel/debug/fail_page_alloc/min-order", "0", false},
    };
    unsigned i;
    for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) {
      if (!write_file(files[i].file, files[i].val)) {
        if (files[i].fatal)
          exit(1);
      }
    }
  }
  
  static void execute_one(void);
  
  #define WAIT_FLAGS __WALL
  
  static void loop(void)
  {
    int iter = 0;
    for (;; iter++) {
      char cwdbuf[32];
      sprintf(cwdbuf, "./%d", iter);
      if (mkdir(cwdbuf, 0777))
        exit(1);
      reset_loop();
      int pid = fork();
      if (pid < 0)
        exit(1);
      if (pid == 0) {
        if (chdir(cwdbuf))
          exit(1);
        setup_test();
        execute_one();
        close_fds();
        exit(0);
      }
      int status = 0;
      uint64_t start = current_time_ms();
      for (;;) {
        if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
          break;
        sleep_ms(1);
        if (current_time_ms() - start < 5000) {
          continue;
        }
        kill_and_wait(pid, &status);
        break;
      }
      remove_dir(cwdbuf);
    }
  }
  
  void execute_one(void)
  {
    memcpy((void*)0x20000140, "efs\000", 4);
    memcpy((void*)0x20000180, "./file0\000", 8);
    *(uint64_t*)0x20000480 = 0;
    *(uint64_t*)0x20000488 = 0;
    *(uint64_t*)0x20000490 = 0x101;
    *(uint8_t*)0x200004c0 = 0;
    inject_fault(56);
    syz_mount_image(0x20000140, 0x20000180, 0, 1, 0x20000480, 0, 0x200004c0);
  }
  int main(void)
  {
    inject_fault(56);
    syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
    syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
    syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
    setup_fault();
    for (procid = 0; procid < 6; procid++) {
      if (fork() == 0) {
        use_temporary_dir();
        do_sandbox_none();
      }
    }
    sleep(1000000);
    return 0;
  }
  