// https://syzkaller.appspot.com/bug?id=02ec3472b31d55cb8cb425aa8e7c0b94acde6503 // autogenerated by syzkaller (http://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 __attribute__((noreturn)) static void doexit(int status) { volatile unsigned i; syscall(__NR_exit_group, status); for (i = 0;; i++) { } } #include #include #include #include #include #include #include const int kFailStatus = 67; const int kRetryStatus = 69; static void fail(const char* msg, ...) { int e = errno; va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, " (errno %d)\n", e); doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus); } static void exitf(const char* msg, ...) { int e = errno; va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, " (errno %d)\n", e); doexit(kRetryStatus); } #define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1) #define BITMASK_LEN_OFF(type, bf_off, bf_len) \ (type)(BITMASK_LEN(type, (bf_len)) << (bf_off)) #define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ if ((bf_off) == 0 && (bf_len) == 0) { \ *(type*)(addr) = (type)(val); \ } else { \ type new_val = *(type*)(addr); \ new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \ new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \ *(type*)(addr) = new_val; \ } static uint64_t current_time_ms() { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)) fail("clock_gettime failed"); return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; } static void use_temporary_dir() { char tmpdir_template[] = "./syzkaller.XXXXXX"; char* tmpdir = mkdtemp(tmpdir_template); if (!tmpdir) fail("failed to mkdtemp"); if (chmod(tmpdir, 0777)) fail("failed to chmod"); if (chdir(tmpdir)) fail("failed to chdir"); } 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_cgroups() { if (mkdir("/syzcgroup", 0777)) { } if (mkdir("/syzcgroup/unified", 0777)) { } if (mount("none", "/syzcgroup/unified", "cgroup2", 0, NULL)) { } if (chmod("/syzcgroup/unified", 0777)) { } if (!write_file("/syzcgroup/unified/cgroup.subtree_control", "+cpu +memory +io +pids +rdma")) { } if (mkdir("/syzcgroup/cpu", 0777)) { } if (mount("none", "/syzcgroup/cpu", "cgroup", 0, "cpuset,cpuacct,perf_event,hugetlb")) { } if (!write_file("/syzcgroup/cpu/cgroup.clone_children", "1")) { } if (chmod("/syzcgroup/cpu", 0777)) { } if (mkdir("/syzcgroup/net", 0777)) { } if (mount("none", "/syzcgroup/net", "cgroup", 0, "net_cls,net_prio,devices,freezer")) { } if (chmod("/syzcgroup/net", 0777)) { } } static void setup_binfmt_misc() { if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:syz0::./file0:")) { } if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz1:M:1:yz1::./file0:POC")) { } } static void loop(); static void sandbox_common() { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); setsid(); struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 160 << 20; setrlimit(RLIMIT_AS, &rlim); rlim.rlim_cur = rlim.rlim_max = 8 << 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); if (unshare(CLONE_NEWNS)) { } if (unshare(CLONE_NEWIPC)) { } if (unshare(0x02000000)) { } if (unshare(CLONE_NEWUTS)) { } if (unshare(CLONE_SYSVSEM)) { } } static int do_sandbox_none(void) { if (unshare(CLONE_NEWPID)) { } int pid = fork(); if (pid < 0) fail("sandbox fork failed"); if (pid) return pid; setup_cgroups(); setup_binfmt_misc(); sandbox_common(); if (unshare(CLONE_NEWNET)) { } loop(); doexit(1); } static void remove_dir(const char* dir) { DIR* dp; struct dirent* ep; int iter = 0; retry: while (umount2(dir, MNT_DETACH) == 0) { } dp = opendir(dir); if (dp == NULL) { if (errno == EMFILE) { exitf("opendir(%s) failed due to NOFILE, exiting", dir); } exitf("opendir(%s) failed", dir); } 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); struct stat st; if (lstat(filename, &st)) exitf("lstat(%s) failed", filename); if (S_ISDIR(st.st_mode)) { remove_dir(filename); continue; } int i; for (i = 0;; i++) { if (unlink(filename) == 0) break; if (errno == EROFS) { break; } if (errno != EBUSY || i > 100) exitf("unlink(%s) failed", filename); if (umount2(filename, MNT_DETACH)) exitf("umount(%s) failed", filename); } } closedir(dp); int i; for (i = 0;; i++) { if (rmdir(dir) == 0) break; if (i < 100) { if (errno == EROFS) { break; } if (errno == EBUSY) { if (umount2(dir, MNT_DETACH)) exitf("umount(%s) failed", dir); continue; } if (errno == ENOTEMPTY) { if (iter < 100) { iter++; goto retry; } } } exitf("rmdir(%s) failed", dir); } } static void execute_one(); extern unsigned long long procid; static void loop() { char cgroupdir[64]; snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); char cgroupdir_cpu[64]; snprintf(cgroupdir_cpu, sizeof(cgroupdir_cpu), "/syzcgroup/cpu/syz%llu", procid); char cgroupdir_net[64]; snprintf(cgroupdir_net, sizeof(cgroupdir_net), "/syzcgroup/net/syz%llu", procid); if (mkdir(cgroupdir, 0777)) { } if (mkdir(cgroupdir_cpu, 0777)) { } if (mkdir(cgroupdir_net, 0777)) { } int pid = getpid(); char procs_file[128]; snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir); if (!write_file(procs_file, "%d", pid)) { } snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir_cpu); if (!write_file(procs_file, "%d", pid)) { } snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir_net); if (!write_file(procs_file, "%d", pid)) { } int iter; for (iter = 0;; iter++) { char cwdbuf[32]; sprintf(cwdbuf, "./%d", iter); if (mkdir(cwdbuf, 0777)) fail("failed to mkdir"); int pid = fork(); if (pid < 0) fail("clone failed"); if (pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); if (chdir(cwdbuf)) fail("failed to chdir"); if (symlink(cgroupdir, "./cgroup")) { } if (symlink(cgroupdir_cpu, "./cgroup.cpu")) { } if (symlink(cgroupdir_net, "./cgroup.net")) { } execute_one(); doexit(0); } int status = 0; uint64_t start = current_time_ms(); for (;;) { int res = waitpid(-1, &status, __WALL | WNOHANG); if (res == pid) { break; } usleep(1000); if (current_time_ms() - start < 3 * 1000) continue; kill(-pid, SIGKILL); kill(pid, SIGKILL); while (waitpid(-1, &status, __WALL) != pid) { } break; } remove_dir(cwdbuf); } } uint64_t r[1] = {0xffffffffffffffff}; unsigned long long procid; void execute_one() { long res = 0; *(uint32_t*)0x20000080 = 1; *(uint32_t*)0x20000084 = 0xfffffefe; *(uint8_t*)0x20000088 = 0; *(uint8_t*)0x20000089 = 3; *(uint8_t*)0x2000008a = 2; *(uint8_t*)0x2000008b = -1; *(uint32_t*)0x2000008c = 0; *(uint64_t*)0x20000090 = 0xffffffff; *(uint64_t*)0x20000098 = 0; *(uint64_t*)0x200000a0 = 3; STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x1000, 0, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x7fff, 1, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 8, 2, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0xbc9, 3, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 7, 4, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0xfffffffffffffffd, 5, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x330c, 6, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 1, 7, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 1, 8, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0xfaa, 9, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 1, 10, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x20, 11, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 9, 12, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 2, 13, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 8, 14, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 2, 15, 2); STORE_BY_BITMASK(uint64_t, 0x200000a8, 7, 17, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 2, 18, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 4, 19, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x1000, 20, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x1f3aba64, 21, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0xffff, 22, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0xffffffffffffff80, 23, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0x1c44955a, 24, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 1, 25, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 2, 26, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 4, 27, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 8, 28, 1); STORE_BY_BITMASK(uint64_t, 0x200000a8, 0, 29, 35); *(uint32_t*)0x200000b0 = 2; *(uint32_t*)0x200000b4 = 6; *(uint64_t*)0x200000b8 = 0x20000040; *(uint64_t*)0x200000c0 = 2; *(uint64_t*)0x200000c8 = 8; *(uint64_t*)0x200000d0 = 8; *(uint32_t*)0x200000d8 = 3; *(uint32_t*)0x200000dc = 0; *(uint64_t*)0x200000e0 = 9; *(uint32_t*)0x200000e8 = 4; *(uint16_t*)0x200000ec = 0x8001; *(uint16_t*)0x200000ee = 0; res = syscall(__NR_perf_event_open, 0x20000080, -1, 2, 0xffffff9c, 2); if (res != -1) r[0] = res; syscall(__NR_ioctl, -1, 0x2405, r[0]); memcpy((void*)0x20000080, "./file0", 8); memcpy((void*)0x20000100, "./file0", 8); memcpy((void*)0x20000140, "affs", 5); syscall(__NR_mount, 0x20000080, 0x20000100, 0x20000140, 2, 0); memcpy((void*)0x200000c0, "./file0", 8); syscall(__NR_mkdir, 0x200000c0, 0); memcpy((void*)0x20000000, "./file0", 8); memcpy((void*)0x20000040, "./file0", 8); memcpy((void*)0x20000200, "overlay", 8); syscall(__NR_mount, 0x20000000, 0x20000040, 0x20000200, 0x1001000, 0); memcpy((void*)0x20000640, "./file1/file0", 14); memcpy((void*)0x20000680, "./file0", 8); memcpy((void*)0x200006c0, "sysfs", 6); syscall(__NR_mount, 0x20000640, 0x20000680, 0x200006c0, 0x2100420, 0); *(uint32_t*)0x20000180 = -1; syscall(__NR_setsockopt, -1, 0x10f, 0x87, 0x20000180, 0x13f); } int main() { syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0); char* cwd = get_current_dir_name(); for (procid = 0; procid < 8; procid++) { if (fork() == 0) { for (;;) { if (chdir(cwd)) fail("failed to chdir"); use_temporary_dir(); int pid = do_sandbox_none(); int status = 0; while (waitpid(pid, &status, __WALL) != pid) { } } } } sleep(1000000); return 0; }