uint64_t util_rndGet(uint64_t min, uint64_t max) { if (min > max) { LOG_F("min:%" PRIu64 " > max:%" PRIu64, min, max); } if (util_urandomFd == -1) { if ((util_urandomFd = open("/dev/urandom", O_RDONLY)) == -1) { PLOG_F("Couldn't open /dev/urandom for writing"); } } if (rndIni == false) { if (files_readFromFd(util_urandomFd, (uint8_t *) & rndX, sizeof(rndX)) == false) { PLOG_F("Couldn't read '%zu' bytes from /dev/urandom", sizeof(rndX)); } rndIni = true; } /* MMIX LCG PRNG */ static const uint64_t a = 6364136223846793005ULL; static const uint64_t c = 1442695040888963407ULL; rndX = (a * rndX + c); return ((rndX % (max - min + 1)) + min); }
pid_t arch_fork(honggfuzz_t * hfuzz, fuzzer_t * fuzzer) { int sv[2]; if (hfuzz->persistent == true) { close(fuzzer->linux.persistentSock); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { LOG_F("socketpair(AF_UNIX, SOCK_STREAM)"); return -1; } } /* * We need to wait for the child to finish with wait() in case we're fuzzing * an external process */ uintptr_t clone_flags = CLONE_UNTRACED; if (hfuzz->linux.pid) { clone_flags = SIGCHLD; } pid_t pid = syscall(__NR_clone, (uintptr_t) clone_flags, NULL, NULL, NULL, (uintptr_t) 0); if (hfuzz->persistent == true) { if (pid == -1) { close(sv[0]); close(sv[1]); } if (pid == 0) { if (dup2(sv[1], 1023) == -1) { PLOG_F("dup2('%d', '%d')", sv[1], 1023); } close(sv[0]); close(sv[1]); } if (pid > 0) { close(sv[1]); fuzzer->linux.persistentSock = sv[0]; struct f_owner_ex fown = {.type = F_OWNER_TID,.pid = syscall(__NR_gettid), }; if (fcntl(fuzzer->linux.persistentSock, F_SETOWN_EX, &fown)) { PLOG_F("fcntl(%d, F_SETOWN_EX)", fuzzer->linux.persistentSock); } if (fcntl(fuzzer->linux.persistentSock, F_SETSIG, SIGNAL_WAKE) == -1) { PLOG_F("fcntl(%d, F_SETSIG, SIGNAL_WAKE)", fuzzer->linux.persistentSock); } if (fcntl(fuzzer->linux.persistentSock, F_SETFL, O_ASYNC) == -1) { PLOG_F("fcntl(%d, F_SETFL, O_ASYNC)", fuzzer->linux.persistentSock); } } }
int main(int argc, char *argv[]) { struct nsjconf_t nsjconf; if (!cmdlineParse(argc, argv, &nsjconf)) { exit(1); } if (nsjconf.clone_newuser == false && geteuid() != 0) { LOG_W("--disable_clone_newuser requires root() privs"); } if (nsjconf.daemonize && (daemon(0, 0) == -1)) { PLOG_F("daemon"); } cmdlineLogParams(&nsjconf); if (nsjailSetSigHandlers() == false) { exit(1); } if (nsjailSetTimer() == false) { exit(1); } if (nsjconf.mode == MODE_LISTEN_TCP) { nsjailListenMode(&nsjconf); } else { return nsjailStandaloneMode(&nsjconf); } return 0; }
extern int64_t util_timeNowMillis(void) { struct timeval tv; if (gettimeofday(&tv, NULL) == -1) { PLOG_F("gettimeofday()"); } return (((int64_t) tv.tv_sec * 1000LL) + ((int64_t) tv.tv_usec / 1000LL)); }
rlim_t cmdlineParseRLimit(int res, const char* optarg, unsigned long mul) { struct rlimit cur; if (getrlimit(res, &cur) == -1) { PLOG_F("getrlimit(%d)", res); } if (strcasecmp(optarg, "max") == 0) { return cur.rlim_max; } if (strcasecmp(optarg, "def") == 0) { return cur.rlim_cur; } if (util_isANumber(optarg) == false) { LOG_F("RLIMIT %d needs a numeric or 'max'/'def' value ('%s' provided)", res, optarg); } rlim_t val = strtoul(optarg, NULL, 0) * mul; if ((unsigned long)val == ULONG_MAX && errno != 0) { PLOG_F("strtoul('%s', 0)", optarg); } return val; }
bool arch_archInit(honggfuzz_t * hfuzz) { if (hfuzz->dynFileMethod != _HF_DYNFILE_NONE) { unsigned long major = 0, minor = 0; char *p = NULL; /* * Check that Linux kernel is compatible * * Compatibility list: * 1) Perf exclude_callchain_kernel requires kernel >= 3.7 * TODO: Runtime logic to disable it for unsupported kernels * if it doesn't affect perf counters processing * 2) If 'PERF_TYPE_HARDWARE' is not supported by kernel, ENOENT * is returned from perf_event_open(). Unfortunately, no reliable * way to detect it here. libperf exports some list functions, * although small guarantees it's installed. Maybe a more targeted * message at perf_event_open() error handling will help. */ struct utsname uts; if (uname(&uts) == -1) { PLOG_F("uname() failed"); return false; } p = uts.release; major = strtoul(p, &p, 10); if (*p++ != '.') { LOG_F("Unsupported kernel version (%s)", uts.release); return false; } minor = strtoul(p, &p, 10); if ((major < 3) || ((major == 3) && (minor < 7))) { LOG_E("Unsupported kernel version (%s)", uts.release); return false; } } #if defined(__ANDROID__) && defined(__arm__) /* * For ARM kernels running Android API <= 21, if fuzzing target links to * libcrypto (OpenSSL), OPENSSL_cpuid_setup initialization is triggering a * SIGILL/ILLOPC at armv7_tick() due to "mrrc p15, #1, r0, r1, c14)" instruction. * Setups using BoringSSL (API >= 22) are not affected. */ if (setenv("OPENSSL_armcap", OPENSSL_ARMCAP_ABI, 1) == -1) { PLOG_E("setenv(OPENSSL_armcap) failed"); return false; } #endif return true; }
void report_Report(run_t* run) { if (run->report[0] == '\0') { return; } MX_SCOPED_LOCK(&run->global->cfg.report_mutex); if (reportFD == -1) { char reportFName[PATH_MAX]; if (run->global->cfg.reportFile == NULL) { snprintf(reportFName, sizeof(reportFName), "%s/%s", run->global->io.workDir, _HF_REPORT_FILE); } else { snprintf(reportFName, sizeof(reportFName), "%s", run->global->cfg.reportFile); } reportFD = TEMP_FAILURE_RETRY(open(reportFName, O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, 0644)); if (reportFD == -1) { PLOG_F("Couldn't open('%s') for writing", reportFName); } } char localtmstr[PATH_MAX]; util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr), time(NULL)); dprintf(reportFD, "=====================================================================\n" "TIME: %s\n" "=====================================================================\n" "FUZZER ARGS:\n" " mutationsPerRun : %u\n" " externalCmd : %s\n" " fuzzStdin : %s\n" " timeout : %ld (sec)\n" #if defined(_HF_ARCH_LINUX) || defined(_HF_ARCH_NETBSD) " ignoreAddr : %p\n" #endif " ASLimit : %" PRIu64 " (MiB)\n" " RSSLimit : %" PRIu64 " (MiB)\n" " DATALimit : %" PRIu64 " (MiB)\n" " wordlistFile : %s\n", localtmstr, run->global->mutate.mutationsPerRun, run->global->exe.externalCommand == NULL ? "NULL" : run->global->exe.externalCommand, run->global->exe.fuzzStdin ? "TRUE" : "FALSE", run->global->timing.tmOut, #if defined(_HF_ARCH_LINUX) run->global->linux.ignoreAddr, #elif defined(_HF_ARCH_NETBSD) run->global->netbsd.ignoreAddr, #endif run->global->exe.asLimit, run->global->exe.rssLimit, run->global->exe.dataLimit, run->global->mutate.dictionaryFile == NULL ? "NULL" : run->global->mutate.dictionaryFile); #if defined(_HF_ARCH_LINUX) report_printdynFileMethod(run); #endif report_printTargetCmd(run); dprintf(reportFD, "%s" "=====================================================================\n", run->report); }
static bool arch_perfOpen(pid_t pid, dynFileMethod_t method, int *perfFd) { LOG_D("Enabling PERF for PID=%d (mmapBufSz=%zu), method=%x", pid, perfMmapSz, method); perfDynamicMethod = method; perfBranchesCnt = 0; perfRecordsLost = 0; struct perf_event_attr pe; memset(&pe, 0, sizeof(struct perf_event_attr)); pe.size = sizeof(struct perf_event_attr); pe.disabled = 0; pe.exclude_kernel = 1; pe.exclude_hv = 1; pe.exclude_callchain_kernel = 1; pe.enable_on_exec = 1; pe.type = PERF_TYPE_HARDWARE; switch (method) { case _HF_DYNFILE_INSTR_COUNT: LOG_D("Using: PERF_COUNT_HW_INSTRUCTIONS for PID: %d", pid); pe.config = PERF_COUNT_HW_INSTRUCTIONS; pe.inherit = 1; break; case _HF_DYNFILE_BRANCH_COUNT: LOG_D("Using: PERF_COUNT_HW_BRANCH_INSTRUCTIONS for PID: %d", pid); pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; pe.inherit = 1; break; case _HF_DYNFILE_UNIQUE_BLOCK_COUNT: LOG_D("Using: PERF_SAMPLE_BRANCH_STACK/PERF_SAMPLE_IP for PID: %d", pid); pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; pe.sample_type = PERF_SAMPLE_IP; pe.sample_period = 1; /* It's BTS based, so must be equal to 1 */ pe.watermark = 1; pe.wakeup_events = perfMmapSz / 2; break; case _HF_DYNFILE_UNIQUE_EDGE_COUNT: LOG_D("Using: PERF_SAMPLE_BRANCH_STACK/PERF_SAMPLE_IP|PERF_SAMPLE_ADDR for PID: %d", pid); pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ADDR; pe.sample_period = 1; /* It's BTS based, so must be equal to 1 */ pe.watermark = 1; pe.wakeup_events = perfMmapSz / 2; break; default: LOG_E("Unknown perf mode: '%d' for PID: %d", method, pid); return false; break; } *perfFd = perf_event_open(&pe, pid, -1, -1, 0); if (*perfFd == -1) { if (method == _HF_DYNFILE_UNIQUE_BLOCK_COUNT || method == _HF_DYNFILE_UNIQUE_EDGE_COUNT) { LOG_E ("'-LDp'/'-LDe' mode (sample IP/jump) requires LBR/BTS, which present in Intel Haswell " "and newer CPUs (i.e. not in AMD CPUs)"); } PLOG_F("perf_event_open() failed"); return false; } if (method != _HF_DYNFILE_UNIQUE_BLOCK_COUNT && method != _HF_DYNFILE_UNIQUE_EDGE_COUNT) { return true; } perfBloom = mmap(NULL, _HF_PERF_BLOOM_SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); if (perfBloom == MAP_FAILED) { perfBloom = NULL; PLOG_E("mmap(size=%zu) failed", (size_t) _HF_PERF_BLOOM_SZ); } perfMmapBuf = mmap(NULL, perfMmapSz + getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0); if (perfMmapBuf == MAP_FAILED) { perfMmapBuf = NULL; PLOG_E("mmap() failed"); close(*perfFd); return false; } struct sigaction sa = { .sa_handler = arch_perfSigHandler, .sa_flags = SA_RESTART, }; sigemptyset(&sa.sa_mask); if (sigaction(_HF_RT_SIG, &sa, NULL) == -1) { PLOG_E("sigaction() failed"); return false; } if (fcntl(*perfFd, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC) == -1) { PLOG_E("fnctl(F_SETFL)"); close(*perfFd); return false; } if (fcntl(*perfFd, F_SETSIG, _HF_RT_SIG) == -1) { PLOG_E("fnctl(F_SETSIG)"); close(*perfFd); return false; } struct f_owner_ex foe = { .type = F_OWNER_TID, .pid = syscall(__NR_gettid) }; if (fcntl(*perfFd, F_SETOWN_EX, &foe) == -1) { PLOG_E("fnctl(F_SETOWN_EX)"); close(*perfFd); return false; } return true; }
extern void MX_UNLOCK(pthread_mutex_t * mutex) { if (pthread_mutex_unlock(mutex)) { PLOG_F("pthread_mutex_unlock(%p)", mutex); } }
void arch_reapChild(honggfuzz_t * hfuzz, fuzzer_t * fuzzer) { pid_t ptracePid = (hfuzz->pid > 0) ? hfuzz->pid : fuzzer->pid; pid_t childPid = fuzzer->pid; timer_t timerid; if (arch_setTimer(&timerid) == false) { LOG_F("Couldn't set timer"); } if (arch_ptraceWaitForPidStop(childPid) == false) { LOG_F("PID %d not in a stopped state", childPid); } LOG_D("PID: %d is in a stopped state now", childPid); static bool ptraceAttached = false; if (ptraceAttached == false) { if (arch_ptraceAttach(ptracePid) == false) { LOG_F("arch_ptraceAttach(pid=%d) failed", ptracePid); } /* In case we fuzz a long-lived process (-p pid) we attach to it once only */ if (ptracePid != childPid) { ptraceAttached = true; } } /* A long-lived processed could have already exited, and we wouldn't know */ if (kill(ptracePid, 0) == -1) { PLOG_F("Liveness of %d questioned", ptracePid); } perfFd_t perfFds; if (arch_perfEnable(ptracePid, hfuzz, &perfFds) == false) { LOG_F("Couldn't enable perf counters for pid %d", ptracePid); } if (kill(childPid, SIGCONT) == -1) { PLOG_F("Restarting PID: %d failed", childPid); } for (;;) { int status; pid_t pid = wait4(-1, &status, __WALL | __WNOTHREAD, NULL); if (pid == -1 && errno == EINTR) { if (hfuzz->tmOut) { arch_checkTimeLimit(hfuzz, fuzzer); } continue; } if (pid == -1 && errno == ECHILD) { LOG_D("No more processes to track"); break; } if (pid == -1) { PLOG_F("wait4() failed"); } LOG_D("PID '%d' returned with status '%d'", pid, status); arch_ptraceGetCustomPerf(hfuzz, ptracePid, &fuzzer->hwCnts.customCnt); if (ptracePid == childPid) { arch_ptraceAnalyze(hfuzz, status, pid, fuzzer); continue; } if (pid == childPid && (WIFEXITED(status) || WIFSIGNALED(status))) { break; } if (pid == childPid) { continue; } arch_ptraceAnalyze(hfuzz, status, pid, fuzzer); } arch_removeTimer(&timerid); arch_perfAnalyze(hfuzz, fuzzer, &perfFds); return; }
int main(int argc, char *argv[]) { struct nsjconf_t nsjconf = { .hostname = "NSJAIL", .chroot = "/chroot", .argv = NULL, .port = 31337, .uid = -1, .gid = -1, .daemonize = false, .tlimit = 0, .apply_sandbox = true, .verbose = false, .keep_caps = false, .rl_as = 512 * (1024 * 1024), .rl_core = 0, .rl_cpu = 600, .rl_fsize = 1 * (1024 * 1024), .rl_nofile = 32, .rl_nproc = cmdlineParseRLimit(RLIMIT_NPROC, "def", 1), .rl_stack = cmdlineParseRLimit(RLIMIT_STACK, "def", 1), .personality = 0, .clone_newnet = true, .clone_newuser = true, .clone_newns = true, .clone_newpid = true, .clone_newipc = true, .clone_newuts = true, .mode = MODE_LISTEN_TCP, .is_root_rw = false, .is_silent = false, .bindmountpts = NULL, .tmpfsmountpts = NULL, .initial_uid = getuid(), .initial_gid = getgid(), .max_conns_per_ip = 0, }; if (!cmdlineParse(argc, argv, &nsjconf)) { exit(1); } if (nsjconf.clone_newuser == false && geteuid() != 0) { LOG_E("--disable_clone_newuser requires root() privs"); } if (nsjconf.daemonize && (daemon(0, 0) == -1)) { PLOG_F("daemon"); } cmdlineLogParams(&nsjconf); if (nsjailSetSigHandlers() == false) { exit(1); } if (nsjailSetTimer() == false) { exit(1); } if (nsjconf.mode == MODE_LISTEN_TCP) { nsjailListenMode(&nsjconf); } else { nsjailStandaloneMode(&nsjconf); } return 0; }
static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index) { int dstfd = open(fuzzer->fileName, O_CREAT | O_EXCL | O_RDWR, 0644); if (dstfd == -1) { PLOG_E("Couldn't create a temporary file '%s'", fuzzer->fileName); return false; } LOG_D("Created '%s' as an input file", fuzzer->fileName); if (hfuzz->inputFile) { size_t fileSz = files_readFileToBufMax(hfuzz->files[rnd_index], fuzzer->dynamicFile, hfuzz->maxFileSz); if (fileSz == 0UL) { LOG_E("Couldn't read '%s'", hfuzz->files[rnd_index]); unlink(fuzzer->fileName); return false; } if (files_writeToFd(dstfd, fuzzer->dynamicFile, fileSz) == false) { close(dstfd); unlink(fuzzer->fileName); return false; } } close(dstfd); pid_t pid = arch_fork(hfuzz); if (pid == -1) { PLOG_E("Couldn't fork"); return false; } if (!pid) { /* * child performs the external file modifications */ execl(hfuzz->externalCommand, hfuzz->externalCommand, fuzzer->fileName, NULL); PLOG_F("Couldn't execute '%s %s'", hfuzz->externalCommand, fuzzer->fileName); return false; } /* * parent waits until child is done fuzzing the input file */ int childStatus; int flags = 0; #if defined(__WNOTHREAD) flags |= __WNOTHREAD; #endif /* defined(__WNOTHREAD) */ while (wait4(pid, &childStatus, flags, NULL) != pid) ; if (WIFEXITED(childStatus)) { LOG_D("External command exited with status %d", WEXITSTATUS(childStatus)); return true; } if (WIFSIGNALED(childStatus)) { LOG_E("External command terminated with signal %d", WTERMSIG(childStatus)); return false; } LOG_F("External command terminated abnormally, status: %d", childStatus); return false; abort(); /* NOTREACHED */ }