void SysResourceTest::CheckResourceLimits() { ASSERT_EQ(0, getrlimit(RLIMIT_CORE, &l32_)); ASSERT_EQ(0, getrlimit64(RLIMIT_CORE, &l64_)); ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, NULL, &pr_l64_)); ASSERT_EQ(l64_.rlim_cur, pr_l64_.rlim_cur); if (l64_.rlim_cur == RLIM64_INFINITY) { ASSERT_EQ(RLIM_INFINITY, l32_.rlim_cur); } else { ASSERT_EQ(l64_.rlim_cur, l32_.rlim_cur); } ASSERT_EQ(l64_.rlim_max, pr_l64_.rlim_max); if (l64_.rlim_max == RLIM64_INFINITY) { ASSERT_EQ(RLIM_INFINITY, l32_.rlim_max); } else { ASSERT_EQ(l64_.rlim_max, l32_.rlim_max); } }
TEST_F(SysResourceTest, prlimit64) { pr_l64_.rlim_cur = pr_l64_.rlim_max; ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, &pr_l64_, NULL)); CheckResourceLimits(); ASSERT_EQ(pr_l64_.rlim_max, pr_l64_.rlim_cur); }
virtual void SetUp() { ASSERT_EQ(0, getrlimit(RLIMIT_CORE, &l32_)); ASSERT_EQ(0, getrlimit64(RLIMIT_CORE, &l64_)); ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, NULL, &pr_l64_)); }
void SetUp() override { ASSERT_EQ(0, getrlimit(RLIMIT_CORE, &l32_)); ASSERT_EQ(0, getrlimit64(RLIMIT_CORE, &l64_)); ASSERT_EQ(0, prlimit(0, RLIMIT_CORE, nullptr, &pr_l32_)); ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, nullptr, &pr_l64_)); }
static bool containDropPrivs(struct nsjconf_t *nsjconf) { /* * Best effort because of /proc/self/setgroups */ gid_t *group_list = NULL; if (setgroups(0, group_list) == -1) { PLOG_D("setgroups(NULL) failed"); } if (setresgid(nsjconf->inside_gid, nsjconf->inside_gid, nsjconf->inside_gid) == -1) { PLOG_E("setresgid(%u)", nsjconf->inside_gid); return false; } if (setresuid(nsjconf->inside_uid, nsjconf->inside_uid, nsjconf->inside_uid) == -1) { PLOG_E("setresuid(%u)", nsjconf->inside_uid); return false; } #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 #endif if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { /* Only new kernels support it */ PLOG_W("prctl(PR_SET_NO_NEW_PRIVS, 1)"); } if (nsjconf->keep_caps == false) { for (unsigned long i = 0; i < 128UL; i++) { /* * Number of capabilities differs between kernels, so * wait for the first one which returns EINVAL */ if (prctl(PR_CAPBSET_DROP, i, 0UL, 0UL, 0UL) == -1 && errno == EINVAL) { break; } } if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1) { PLOG_E("prctl(PR_SET_KEEPCAPS, 0)"); return false; } struct __user_cap_header_struct cap_hdr = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = 0, }; const struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3] = { [0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].inheritable = 0U, [0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].effective = 0U, [0 ... (_LINUX_CAPABILITY_U32S_3 - 1)].permitted = 0U, }; if (syscall(__NR_capset, &cap_hdr, &cap_data) == -1) { PLOG_E("capset()"); return false; } } return true; } static bool containPrepareEnv(struct nsjconf_t *nsjconf) { if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) { PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL)"); return false; } if (nsjconf->personality && personality(nsjconf->personality) == -1) { PLOG_E("personality(%lx)", nsjconf->personality); return false; } errno = 0; if (setpriority(PRIO_PROCESS, 0, 19) == -1 && errno != 0) { PLOG_W("setpriority(19)"); } if (nsjconf->skip_setsid == false) { setsid(); } return true; } static bool containInitMountNs(struct nsjconf_t *nsjconf) { return mountInitNs(nsjconf); } static bool containSetLimits(struct nsjconf_t *nsjconf) { struct rlimit64 rl; rl.rlim_cur = rl.rlim_max = nsjconf->rl_as; if (prlimit64(0, RLIMIT_AS, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_AS, %" PRIu64 ")", nsjconf->rl_as); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_core; if (prlimit64(0, RLIMIT_CORE, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_CORE, %" PRIu64 ")", nsjconf->rl_core); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_cpu; if (prlimit64(0, RLIMIT_CPU, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_CPU, %" PRIu64 ")", nsjconf->rl_cpu); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_fsize; if (prlimit64(0, RLIMIT_FSIZE, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_FSIZE, %" PRIu64 ")", nsjconf->rl_fsize); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_nofile; if (prlimit64(0, RLIMIT_NOFILE, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_NOFILE, %" PRIu64 ")", nsjconf->rl_nofile); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_nproc; if (prlimit64(0, RLIMIT_NPROC, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_NPROC, %" PRIu64 ")", nsjconf->rl_nproc); return false; } rl.rlim_cur = rl.rlim_max = nsjconf->rl_stack; if (prlimit64(0, RLIMIT_STACK, &rl, NULL) == -1) { PLOG_E("prlimit64(0, RLIMIT_STACK, %" PRIu64 ")", nsjconf->rl_stack); return false; } return true; } static bool containMakeFdsCOENaive(void) { // Don't use getrlimit(RLIMIT_NOFILE) here, as it can return an artifically small value // (e.g. 32), which could be smaller than a maximum assigned number to file-descriptors // in this process. Just use some reasonably sane value (e.g. 1024) for (unsigned fd = (STDERR_FILENO + 1); fd < 1024; fd++) { int flags = fcntl(fd, F_GETFD, 0); if (flags == -1) { continue; } fcntl(fd, F_SETFD, flags | FD_CLOEXEC); LOG_D("Set fd '%d' flag to FD_CLOEXEC", fd); } return true; } static bool containMakeFdsCOEProc(void) { /* Make all fds above stderr close-on-exec */ DIR *dir = opendir("/proc/self/fd"); if (dir == NULL) { PLOG_D("opendir('/proc/self/fd')"); return false; } defer { closedir(dir); }; for (;;) { errno = 0; struct dirent *entry = readdir(dir); if (entry == NULL && errno != 0) { PLOG_D("readdir('/proc/self/fd')"); return false; } if (entry == NULL) { break; } if (strcmp(".", entry->d_name) == 0) { continue; } if (strcmp("..", entry->d_name) == 0) { continue; } int fd = strtoul(entry->d_name, NULL, 10); if (errno == EINVAL) { LOG_W("Cannot convert /proc/self/fd/%s to a number", entry->d_name); continue; } if (fd > STDERR_FILENO) { int flags = fcntl(fd, F_GETFD, 0); if (flags == -1) { PLOG_D("fcntl(fd, F_GETFD, 0)"); return false; } fcntl(fd, F_SETFD, flags | FD_CLOEXEC); LOG_D("Set fd '%d' flag to FD_CLOEXEC", fd); } } return true; }
// There is no setrlimit64 system call, so we need to use prlimit64. int setrlimit64(int resource, const rlimit64* limits64) { return prlimit64(0, resource, limits64, NULL); }
// There is no getrlimit64 system call, so we need to use prlimit64. int getrlimit64(int resource, rlimit64* limits64) { return prlimit64(0, resource, NULL, limits64); }
bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName) { /* * Kill the children when fuzzer dies (e.g. due to Ctrl+C) */ if (prctl(PR_SET_PDEATHSIG, (long)SIGKILL, 0L, 0L, 0L) == -1) { PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL) failed"); return false; } /* * Kill a process which corrupts its own heap (with ABRT) */ if (setenv("MALLOC_CHECK_", "3", 1) == -1) { PLOG_E("setenv(MALLOC_CHECK_=3) failed"); return false; } /* * Tell asan to ignore SEGVs */ if (setenv ("ASAN_OPTIONS", "allow_user_segv_handler=1:handle_segv=0:abort_on_error=1:allocator_may_return_null=1", 1) == -1) { PLOG_E("setenv(ASAN_OPTIONS) failed"); return false; } const char *msan_options = "exit_code=" HF_MSAN_EXIT_CODE_STR ":report_umrs=0:wrap_signals=0:print_stats=1"; if (hfuzz->msanReportUMRS == true) { msan_options = "exit_code=" HF_MSAN_EXIT_CODE_STR ":report_umrs=1:wrap_signals=0:print_stats=1"; } if (setenv("MSAN_OPTIONS", msan_options, 1) == -1) { PLOG_E("setenv(MSAN_OPTIONS) failed"); return false; } /* * Disable ASLR */ if (hfuzz->disableRandomization && personality(ADDR_NO_RANDOMIZE) == -1) { PLOG_E("personality(ADDR_NO_RANDOMIZE) failed"); return false; } #define ARGS_MAX 512 char *args[ARGS_MAX + 2]; char argData[PATH_MAX] = { 0 }; int x; for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) { if (!hfuzz->fuzzStdin && strcmp(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER) == 0) { args[x] = fileName; } else if (!hfuzz->fuzzStdin && strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER)) { const char *off = strstr(hfuzz->cmdline[x], _HF_FILE_PLACEHOLDER); snprintf(argData, PATH_MAX, "%.*s%s", (int)(off - hfuzz->cmdline[x]), hfuzz->cmdline[x], fileName); args[x] = argData; } else { args[x] = hfuzz->cmdline[x]; } } args[x++] = NULL; LOG_D("Launching '%s' on file '%s'", args[0], fileName); /* * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof) */ if (hfuzz->tmOut) { /* * Set the CPU rlimit to twice the value of the time-out */ struct rlimit rl = { .rlim_cur = hfuzz->tmOut * 2, .rlim_max = hfuzz->tmOut * 2, }; if (setrlimit(RLIMIT_CPU, &rl) == -1) { PLOG_E("Couldn't enforce the RLIMIT_CPU resource limit"); return false; } } /* * The address space limit. If big enough - roughly the size of RAM used */ if (hfuzz->asLimit) { struct rlimit64 rl = { .rlim_cur = hfuzz->asLimit * 1024ULL * 1024ULL, .rlim_max = hfuzz->asLimit * 1024ULL * 1024ULL, }; if (prlimit64(0, RLIMIT_AS, &rl, NULL) == -1) { PLOG_D("Couldn't enforce the RLIMIT_AS resource limit, ignoring"); } } for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs) && hfuzz->envs[i]; i++) { putenv(hfuzz->envs[i]); } if (hfuzz->nullifyStdio) { util_nullifyStdio(); } if (hfuzz->fuzzStdin) { /* * Uglyyyyyy ;) */ if (!util_redirectStdin(fileName)) { return false; } } /* * Wait for the ptrace to attach */ syscall(__NR_tkill, syscall(__NR_gettid), SIGSTOP); execvp(args[0], args); util_recoverStdio(); LOG_F("Failed to create new '%s' process", args[0]); return false; } static void arch_sigFunc(int signo, siginfo_t * si, void *dummy) { if (signo != SIGALRM) { LOG_E("Signal != SIGALRM (%d)", signo); } return; if (si == NULL) { return; } if (dummy == NULL) { return; } } static void arch_removeTimer(timer_t * timerid) { timer_delete(*timerid); } static bool arch_setTimer(timer_t * timerid) { struct sigevent sevp = { .sigev_value.sival_ptr = timerid, .sigev_signo = SIGALRM, .sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL, ._sigev_un._tid = syscall(__NR_gettid), }; if (timer_create(CLOCK_REALTIME, &sevp, timerid) == -1) { PLOG_E("timer_create(CLOCK_REALTIME) failed"); return false; } /* * Kick in every 200ms, starting with the next second */ const struct itimerspec ts = { .it_value = {.tv_sec = 1,.tv_nsec = 0}, .it_interval = {.tv_sec = 0,.tv_nsec = 200000000,}, }; if (timer_settime(*timerid, 0, &ts, NULL) == -1) { PLOG_E("timer_settime() failed"); timer_delete(*timerid); return false; } sigset_t smask; sigemptyset(&smask); struct sigaction sa = { .sa_handler = NULL, .sa_sigaction = arch_sigFunc, .sa_mask = smask, .sa_flags = SA_SIGINFO, .sa_restorer = NULL, }; if (sigaction(SIGALRM, &sa, NULL) == -1) { PLOG_E("sigaction(SIGALRM) failed"); return false; } return true; } static void arch_checkTimeLimit(honggfuzz_t * hfuzz, fuzzer_t * fuzzer) { int64_t curMillis = util_timeNowMillis(); int64_t diffMillis = curMillis - fuzzer->timeStartedMillis; if (diffMillis > (hfuzz->tmOut * 1000)) { LOG_W("PID %d took too much time (limit %ld s). Sending SIGKILL", fuzzer->pid, hfuzz->tmOut); kill(fuzzer->pid, SIGKILL); __sync_fetch_and_add(&hfuzz->timeoutedCnt, 1UL); } }