blob: d86bc7e0f8f867ce1c8c8bc2a57b5b807ce07139 [file] [log] [blame]
/*
*
* honggfuzz - fuzzing routines
* -----------------------------------------
*
* Author:
* Robert Swiecki <swiecki@google.com>
* Felix Gröbert <groebert@google.com>
*
* Copyright 2010-2015 by Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/
#include "common.h"
#include "fuzz.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include "arch.h"
#include "display.h"
#include "files.h"
#include "log.h"
#include "mangle.h"
#include "report.h"
#include "util.h"
static int fuzz_sigReceived = 0;
static pthread_t fuzz_mainThread;
static inline bool fuzz_isPerfCntsSet(honggfuzz_t * hfuzz)
{
if (hfuzz->hwCnts.cpuInstrCnt > 0ULL || hfuzz->hwCnts.cpuBranchCnt > 0ULL
|| hfuzz->hwCnts.cpuBtsBlockCnt > 0ULL || hfuzz->hwCnts.cpuBtsEdgeCnt > 0ULL
|| hfuzz->hwCnts.customCnt > 0ULL) {
return true;
} else {
return false;
}
}
static inline void fuzz_resetFeedbackCnts(honggfuzz_t * hfuzz)
{
/* HW perf counters */
__sync_fetch_and_and(&hfuzz->hwCnts.cpuInstrCnt, 0UL);
__sync_fetch_and_and(&hfuzz->hwCnts.cpuBranchCnt, 0UL);
__sync_fetch_and_and(&hfuzz->hwCnts.cpuBtsBlockCnt, 0UL);
__sync_fetch_and_and(&hfuzz->hwCnts.cpuBtsEdgeCnt, 0UL);
__sync_fetch_and_and(&hfuzz->hwCnts.customCnt, 0UL);
/* Sanitizer coverage counter */
__sync_fetch_and_and(&hfuzz->sanCovCnts.hitBBCnt, 0UL);
__sync_fetch_and_and(&hfuzz->sanCovCnts.totalBBCnt, 0UL);
__sync_fetch_and_and(&hfuzz->sanCovCnts.dsoCnt, 0UL);
__sync_fetch_and_and(&hfuzz->sanCovCnts.iDsoCnt, 0UL);
__sync_fetch_and_and(&hfuzz->sanCovCnts.newBBCnt, 0UL);
__sync_fetch_and_and(&hfuzz->sanCovCnts.crashesCnt, 0UL);
/*
* For performance reasons Trie & Bitmap methods are not exposed in arch.h
* Thus maintain a status flag to destroy runtime data internally at sancov.c
* when dynFile input seed is replaced.
*/
hfuzz->clearCovMetadata = true;
}
static void fuzz_sigHandler(int sig)
{
/* We should not terminate upon SIGALRM delivery */
if (sig == SIGALRM) {
return;
}
fuzz_sigReceived = sig;
}
static void fuzz_getFileName(honggfuzz_t * hfuzz, char *fileName)
{
struct timeval tv;
gettimeofday(&tv, NULL);
snprintf(fileName, PATH_MAX, "%s/.honggfuzz.%d.%lu.%llx.%s", hfuzz->workDir, (int)getpid(),
(unsigned long int)tv.tv_sec, (unsigned long long int)util_rndGet(0, 1ULL << 62),
hfuzz->fileExtn);
}
static bool fuzz_prepareFileDynamically(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index)
{
MX_LOCK(&hfuzz->dynamicFile_mutex);
/* If max dynamicFile iterations counter, pick new seed file when working with input file corpus */
if (hfuzz->inputFile &&
__sync_fetch_and_add(&hfuzz->dynFileIterExpire, 0UL) >= _HF_MAX_DYNFILE_ITER) {
size_t fileSz = files_readFileToBufMax(hfuzz->files[rnd_index], hfuzz->dynamicFileBest,
hfuzz->maxFileSz);
if (fileSz == 0) {
MX_UNLOCK(&hfuzz->dynamicFile_mutex);
LOG_E("Couldn't read '%s'", hfuzz->files[rnd_index]);
return false;
}
hfuzz->dynamicFileBestSz = fileSz;
/* Reset counter since new seed pick */
__sync_fetch_and_and(&hfuzz->dynFileIterExpire, 0UL);
fuzz_resetFeedbackCnts(hfuzz);
/*
* In order to have accurate comparison base for coverage, first iteration
* of a new seed is executed without mangling. Also workersBlock_mutex mutex
* is maintain until execution is finished to ensure that other threads will
* work against the same coverage data vs. original seed.
*/
hfuzz->isDynFileLocked = true;
} else if (hfuzz->inputFile == NULL && (fuzz_isPerfCntsSet(hfuzz) == false)) {
/*
* When working with an empty input file corpus (allowed if perf feedback enabled for Linux archs),
* first iteration is executed without mangling. First iteration need to be executed by one thread
* blocking other workers from continuing until finished.
*/
hfuzz->isDynFileLocked = true;
}
if (hfuzz->dynamicFileBestSz > hfuzz->maxFileSz) {
LOG_F("Current BEST file Sz > maxFileSz (%zu > %zu)", hfuzz->dynamicFileBestSz,
hfuzz->maxFileSz);
}
fuzzer->dynamicFileSz = hfuzz->dynamicFileBestSz;
memcpy(fuzzer->dynamicFile, hfuzz->dynamicFileBest, hfuzz->dynamicFileBestSz);
MX_UNLOCK(&hfuzz->dynamicFile_mutex);
/*
* true isDynFileLocked indicates first run for a new seed, so skip mangling
* without unlocking threads block mutex.
*/
MX_LOCK(&hfuzz->workersBlock_mutex);
if (hfuzz->isDynFileLocked) {
goto skipMangling;
}
MX_UNLOCK(&hfuzz->workersBlock_mutex);
/*
* if flip rate is 0.0, early abort file mangling. This will leave perf counters
* with values equal to dry runs against input corpus.
*/
if (hfuzz->flipRate == 0.0L) {
goto skipMangling;
}
mangle_Resize(hfuzz, fuzzer->dynamicFile, &fuzzer->dynamicFileSz);
mangle_mangleContent(hfuzz, fuzzer->dynamicFile, fuzzer->dynamicFileSz);
skipMangling:
if (files_writeBufToFile
(fuzzer->fileName, fuzzer->dynamicFile, fuzzer->dynamicFileSz,
O_WRONLY | O_CREAT | O_EXCL | O_TRUNC) == false) {
LOG_E("Couldn't write buffer to file '%s'", fuzzer->fileName);
return false;
}
return true;
}
static bool fuzz_prepareFile(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index)
{
size_t fileSz =
files_readFileToBufMax(hfuzz->files[rnd_index], fuzzer->dynamicFile, hfuzz->maxFileSz);
if (fileSz == 0UL) {
LOG_E("Couldn't read contents of '%s'", hfuzz->files[rnd_index]);
return false;
}
/* If flip rate is 0.0, early abort file mangling */
if (hfuzz->flipRate != 0.0L) {
mangle_Resize(hfuzz, fuzzer->dynamicFile, &fileSz);
mangle_mangleContent(hfuzz, fuzzer->dynamicFile, fileSz);
}
if (files_writeBufToFile
(fuzzer->fileName, fuzzer->dynamicFile, fileSz, O_WRONLY | O_CREAT | O_EXCL) == false) {
LOG_E("Couldn't write buffer to file '%s'", fuzzer->fileName);
return false;
}
return true;
}
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 */
}
static bool fuzz_runVerifier(honggfuzz_t * hfuzz, fuzzer_t * crashedFuzzer)
{
bool ret = false;
int crashFd = -1;
uint8_t *crashBuf = NULL;
off_t crashFileSz = 0;
crashBuf = files_mapFile(crashedFuzzer->crashFileName, &crashFileSz, &crashFd, false);
if (crashBuf == NULL) {
LOG_E("Couldn't open and map '%s' in R/O mode", crashedFuzzer->crashFileName);
goto bail;
}
LOG_I("Launching verifier for %" PRIx64 " hash", crashedFuzzer->backtrace);
for (int i = 0; i < _HF_VERIFIER_ITER; i++) {
fuzzer_t vFuzzer = {
.pid = 0,
.timeStartedMillis = util_timeNowMillis(),
.crashFileName = {0},
.pc = 0ULL,
.backtrace = 0ULL,
.access = 0ULL,
.exception = 0,
.dynamicFileSz = 0,
.dynamicFile = NULL,
.hwCnts = {
.cpuInstrCnt = 0ULL,
.cpuBranchCnt = 0ULL,
.cpuBtsBlockCnt = 0ULL,
.cpuBtsEdgeCnt = 0ULL,
.customCnt = 0ULL,
},
.sanCovCnts = {
.hitBBCnt = 0ULL,
.totalBBCnt = 0ULL,
.dsoCnt = 0ULL,
.iDsoCnt = 0ULL,
.newBBCnt = 0ULL,
.crashesCnt = 0ULL,
},
.report = {'\0'},
.mainWorker = false
};
fuzz_getFileName(hfuzz, vFuzzer.fileName);
if (files_writeBufToFile
(vFuzzer.fileName, crashBuf, crashFileSz, O_WRONLY | O_CREAT | O_EXCL) == false) {
LOG_E("Couldn't write buffer to file '%s'", vFuzzer.fileName);
goto bail;
}
vFuzzer.pid = arch_fork(hfuzz);
if (vFuzzer.pid == -1) {
PLOG_F("Couldn't fork");
return false;
}
if (!vFuzzer.pid) {
if (!arch_launchChild(hfuzz, crashedFuzzer->crashFileName)) {
LOG_E("Error launching verifier child process");
goto bail;
}
}
arch_reapChild(hfuzz, &vFuzzer);
unlink(vFuzzer.fileName);
/* If stack hash doesn't match skip name tag and exit */
if (crashedFuzzer->backtrace != vFuzzer.backtrace) {
LOG_D("Verifier stack hash mismatch");
goto bail;
}
}
/* Workspace is inherited, just append a extra suffix */
char verFile[PATH_MAX] = { 0 };
snprintf(verFile, sizeof(verFile), "%s.verified", crashedFuzzer->crashFileName);
/* Copy file with new suffix & remove original copy */
bool dstFileExists = false;
if (files_copyFile(crashedFuzzer->crashFileName, verFile, &dstFileExists)) {
LOG_I("Successfully verified, saving as (%s)", verFile);
__sync_fetch_and_add(&hfuzz->verifiedCrashesCnt, 1UL);
unlink(crashedFuzzer->crashFileName);
} else {
if (dstFileExists) {
LOG_I("It seems that '%s' already exists, skipping", verFile);
} else {
LOG_E("Couldn't copy '%s' to '%s'", crashedFuzzer->crashFileName, verFile);
goto bail;
}
}
ret = true;
bail:
if (crashBuf) {
munmap(crashBuf, crashFileSz);
}
if (crashFd != -1) {
close(crashFd);
}
return ret;
}
static void fuzz_perfFeedback(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
{
LOG_D
("File size (New/Best): %zu/%zu, Perf feedback (instr,branch,block,block-edge,custom): Best: [%"
PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "] / New: [%" PRIu64 ",%" PRIu64
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "]", fuzzer->dynamicFileSz,
hfuzz->dynamicFileBestSz, hfuzz->hwCnts.cpuInstrCnt, hfuzz->hwCnts.cpuBranchCnt,
hfuzz->hwCnts.cpuBtsBlockCnt, hfuzz->hwCnts.cpuBtsEdgeCnt, hfuzz->hwCnts.customCnt,
fuzzer->hwCnts.cpuInstrCnt, fuzzer->hwCnts.cpuBranchCnt, fuzzer->hwCnts.cpuBtsBlockCnt,
fuzzer->hwCnts.cpuBtsEdgeCnt, fuzzer->hwCnts.customCnt);
MX_LOCK(&hfuzz->dynamicFile_mutex);
int64_t diff0 = hfuzz->hwCnts.cpuInstrCnt - fuzzer->hwCnts.cpuInstrCnt;
int64_t diff1 = hfuzz->hwCnts.cpuBranchCnt - fuzzer->hwCnts.cpuBranchCnt;
int64_t diff2 = hfuzz->hwCnts.cpuBtsBlockCnt - fuzzer->hwCnts.cpuBtsBlockCnt;
int64_t diff3 = hfuzz->hwCnts.cpuBtsEdgeCnt - fuzzer->hwCnts.cpuBtsEdgeCnt;
int64_t diff4 = hfuzz->hwCnts.cpuIptBlockCnt - fuzzer->hwCnts.cpuIptBlockCnt;
int64_t diff5 = hfuzz->hwCnts.cpuIptEdgeCnt - fuzzer->hwCnts.cpuIptEdgeCnt;
int64_t diff6 = hfuzz->hwCnts.customCnt - fuzzer->hwCnts.customCnt;
if (diff0 < 0 || diff1 < 0 || diff2 < 0 || diff3 < 0 || diff4 < 0 || diff5 < 0 || diff6 < 0) {
LOG_I("New: (Size New,Old): %zu,%zu, Perf (Cur,New): "
"%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64
",%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64,
fuzzer->dynamicFileSz, hfuzz->dynamicFileBestSz,
hfuzz->hwCnts.cpuInstrCnt, hfuzz->hwCnts.cpuBranchCnt,
hfuzz->hwCnts.cpuBtsBlockCnt, hfuzz->hwCnts.cpuBtsEdgeCnt,
hfuzz->hwCnts.cpuIptBlockCnt, hfuzz->hwCnts.cpuIptEdgeCnt, hfuzz->hwCnts.customCnt,
fuzzer->hwCnts.cpuInstrCnt, fuzzer->hwCnts.cpuBranchCnt,
fuzzer->hwCnts.cpuBtsBlockCnt, fuzzer->hwCnts.cpuBtsEdgeCnt,
fuzzer->hwCnts.cpuIptBlockCnt, fuzzer->hwCnts.cpuIptEdgeCnt,
fuzzer->hwCnts.customCnt);
memcpy(hfuzz->dynamicFileBest, fuzzer->dynamicFile, fuzzer->dynamicFileSz);
hfuzz->dynamicFileBestSz = fuzzer->dynamicFileSz;
hfuzz->hwCnts.cpuInstrCnt = fuzzer->hwCnts.cpuInstrCnt;
hfuzz->hwCnts.cpuBranchCnt = fuzzer->hwCnts.cpuBranchCnt;
hfuzz->hwCnts.cpuBtsBlockCnt = fuzzer->hwCnts.cpuBtsBlockCnt;
hfuzz->hwCnts.cpuBtsEdgeCnt = fuzzer->hwCnts.cpuBtsEdgeCnt;
hfuzz->hwCnts.cpuIptBlockCnt = fuzzer->hwCnts.cpuIptBlockCnt;
hfuzz->hwCnts.cpuIptEdgeCnt = fuzzer->hwCnts.cpuIptEdgeCnt;
hfuzz->hwCnts.customCnt = fuzzer->hwCnts.customCnt;
/* Reset counter if better coverage achieved */
__sync_fetch_and_and(&hfuzz->dynFileIterExpire, 0UL);
char currentBest[PATH_MAX], currentBestTmp[PATH_MAX];
snprintf(currentBest, PATH_MAX, "%s/CURRENT_BEST", hfuzz->workDir);
snprintf(currentBestTmp, PATH_MAX, "%s/.tmp.CURRENT_BEST", hfuzz->workDir);
if (files_writeBufToFile
(currentBestTmp, fuzzer->dynamicFile, fuzzer->dynamicFileSz,
O_WRONLY | O_CREAT | O_TRUNC)) {
rename(currentBestTmp, currentBest);
}
}
MX_UNLOCK(&hfuzz->dynamicFile_mutex);
}
static void fuzz_sanCovFeedback(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
{
LOG_D
("File size (Best/New): %zu/%zu, SanCov feedback (bb,dso): Best: [%" PRIu64
",%" PRIu64 "] / New: [%" PRIu64 ",%" PRIu64 "], newBBs:%" PRIu64,
hfuzz->dynamicFileBestSz, fuzzer->dynamicFileSz, hfuzz->sanCovCnts.hitBBCnt,
hfuzz->sanCovCnts.iDsoCnt, fuzzer->sanCovCnts.hitBBCnt, fuzzer->sanCovCnts.iDsoCnt,
fuzzer->sanCovCnts.newBBCnt);
MX_LOCK(&hfuzz->dynamicFile_mutex);
/* abs diff of total BBs between global counter for chosen seed & current run */
uint64_t totalBBsDiff;
if (hfuzz->sanCovCnts.hitBBCnt > fuzzer->sanCovCnts.hitBBCnt) {
totalBBsDiff = hfuzz->sanCovCnts.hitBBCnt - fuzzer->sanCovCnts.hitBBCnt;
} else {
totalBBsDiff = fuzzer->sanCovCnts.hitBBCnt - hfuzz->sanCovCnts.hitBBCnt;
}
/*
* Keep mutated seed if:
* a) Newly discovered (not met before) BBs && total hit BBs not significantly dropped
* b) More instrumented code accessed (BB hit counter bigger)
* c) More instrumented DSOs loaded
*
* TODO: (a) method can significantly assist to further improvements in interesting areas
* discovery if combined with seeds pool/queue support. If a runtime queue is maintained
* more interesting seeds can be saved between runs instead of instantly discarded
* based on current absolute elitism (only one mutated seed is promoted).
*/
if ((fuzzer->sanCovCnts.newBBCnt > 0 && fuzzer->sanCovCnts.newBBCnt >= totalBBsDiff) ||
hfuzz->sanCovCnts.hitBBCnt < fuzzer->sanCovCnts.hitBBCnt ||
hfuzz->sanCovCnts.iDsoCnt < fuzzer->sanCovCnts.iDsoCnt) {
LOG_I("SanCov Update: file size (Cur,New): %zu,%zu, newBBs:%" PRIu64
", counters (Cur,New): %" PRIu64 "/%" PRIu64 ",%" PRIu64 "/%" PRIu64,
hfuzz->dynamicFileBestSz, fuzzer->dynamicFileSz, fuzzer->sanCovCnts.newBBCnt,
hfuzz->sanCovCnts.hitBBCnt, hfuzz->sanCovCnts.iDsoCnt, fuzzer->sanCovCnts.hitBBCnt,
fuzzer->sanCovCnts.iDsoCnt);
memcpy(hfuzz->dynamicFileBest, fuzzer->dynamicFile, fuzzer->dynamicFileSz);
hfuzz->dynamicFileBestSz = fuzzer->dynamicFileSz;
hfuzz->sanCovCnts.hitBBCnt = fuzzer->sanCovCnts.hitBBCnt;
hfuzz->sanCovCnts.dsoCnt = fuzzer->sanCovCnts.dsoCnt;
hfuzz->sanCovCnts.iDsoCnt = fuzzer->sanCovCnts.iDsoCnt;
hfuzz->sanCovCnts.crashesCnt += fuzzer->sanCovCnts.crashesCnt;
hfuzz->sanCovCnts.newBBCnt += fuzzer->sanCovCnts.newBBCnt;
if (hfuzz->sanCovCnts.totalBBCnt < fuzzer->sanCovCnts.totalBBCnt) {
/* Keep only the max value (for dlopen cases) to measure total target coverage */
hfuzz->sanCovCnts.totalBBCnt = fuzzer->sanCovCnts.totalBBCnt;
}
/* Reset counter if better coverage achieved */
__sync_fetch_and_and(&hfuzz->dynFileIterExpire, 0UL);
char currentBest[PATH_MAX], currentBestTmp[PATH_MAX];
snprintf(currentBest, PATH_MAX, "%s/CURRENT_BEST", hfuzz->workDir);
snprintf(currentBestTmp, PATH_MAX, "%s/.tmp.CURRENT_BEST", hfuzz->workDir);
if (files_writeBufToFile
(currentBestTmp, fuzzer->dynamicFile, fuzzer->dynamicFileSz,
O_WRONLY | O_CREAT | O_TRUNC)) {
rename(currentBestTmp, currentBest);
}
}
MX_UNLOCK(&hfuzz->dynamicFile_mutex);
}
static void fuzz_fuzzLoop(honggfuzz_t * hfuzz)
{
fuzzer_t fuzzer = {
.pid = 0,
.timeStartedMillis = util_timeNowMillis(),
.crashFileName = {0},
.pc = 0ULL,
.backtrace = 0ULL,
.access = 0ULL,
.exception = 0,
.dynamicFileSz = 0,
.dynamicFile = malloc(hfuzz->maxFileSz),
.hwCnts = {
.cpuInstrCnt = 0ULL,
.cpuBranchCnt = 0ULL,
.cpuBtsBlockCnt = 0ULL,
.cpuBtsEdgeCnt = 0ULL,
.customCnt = 0ULL,
},
.sanCovCnts = {
.hitBBCnt = 0ULL,
.totalBBCnt = 0ULL,
.dsoCnt = 0ULL,
.iDsoCnt = 0ULL,
.newBBCnt = 0ULL,
.crashesCnt = 0ULL,
},
.report = {'\0'},
.mainWorker = true
};
if (fuzzer.dynamicFile == NULL) {
LOG_F("malloc(%zu) failed", hfuzz->maxFileSz);
}
size_t rnd_index = util_rndGet(0, hfuzz->fileCnt - 1);
/* If dry run mode, pick the next file and not a random one */
if (hfuzz->flipRate == 0.0L && hfuzz->useVerifier) {
rnd_index = __sync_fetch_and_add(&hfuzz->lastCheckedFileIndex, 1UL);
}
strncpy(fuzzer.origFileName, files_basename(hfuzz->files[rnd_index]), PATH_MAX);
fuzz_getFileName(hfuzz, fuzzer.fileName);
if (hfuzz->dynFileMethod != _HF_DYNFILE_NONE || hfuzz->useSanCov) {
if (!fuzz_prepareFileDynamically(hfuzz, &fuzzer, rnd_index)) {
exit(EXIT_FAILURE);
}
} else if (hfuzz->externalCommand != NULL) {
if (!fuzz_prepareFileExternally(hfuzz, &fuzzer, rnd_index)) {
exit(EXIT_FAILURE);
}
} else {
if (!fuzz_prepareFile(hfuzz, &fuzzer, rnd_index)) {
exit(EXIT_FAILURE);
}
}
fuzzer.pid = arch_fork(hfuzz);
if (fuzzer.pid == -1) {
PLOG_F("Couldn't fork");
exit(EXIT_FAILURE);
}
if (!fuzzer.pid) {
/*
* Ok, kill the parent if this fails
*/
if (!arch_launchChild(hfuzz, fuzzer.fileName)) {
LOG_E("Error launching child process, killing parent");
exit(EXIT_FAILURE);
}
}
LOG_D("Launched new process, pid: %d, (concurrency: %zd)", fuzzer.pid, hfuzz->threadsMax);
arch_reapChild(hfuzz, &fuzzer);
unlink(fuzzer.fileName);
if (hfuzz->dynFileMethod != _HF_DYNFILE_NONE) {
fuzz_perfFeedback(hfuzz, &fuzzer);
} else if (hfuzz->useSanCov) {
fuzz_sanCovFeedback(hfuzz, &fuzzer);
}
/*
* If worker picked first iteration of new seed for dynFile, unlock the mutex
* so other threads can continue.
*/
if (hfuzz->isDynFileLocked) {
hfuzz->isDynFileLocked = false;
MX_UNLOCK(&hfuzz->workersBlock_mutex);
}
if (hfuzz->useVerifier && (fuzzer.crashFileName[0] != 0) && fuzzer.backtrace) {
if (!fuzz_runVerifier(hfuzz, &fuzzer)) {
LOG_I("Failed to verify %s", fuzzer.crashFileName);
}
}
report_Report(hfuzz, fuzzer.report);
free(fuzzer.dynamicFile);
}
static void *fuzz_threadNew(void *arg)
{
honggfuzz_t *hfuzz = (honggfuzz_t *) arg;
for (;;) {
/* Dynamic file iteration counter for same seed */
__sync_fetch_and_add(&hfuzz->dynFileIterExpire, 1UL);
/* Check if dry run mode with verifier enabled */
if (hfuzz->flipRate == 0.0L && hfuzz->useVerifier) {
if (__sync_fetch_and_add(&hfuzz->mutationsCnt, 1UL) >= hfuzz->fileCnt) {
__sync_fetch_and_add(&hfuzz->threadsFinished, 1UL);
// All files checked, weak-up the main process
pthread_kill(fuzz_mainThread, SIGALRM);
return NULL;
}
}
/* Check for max iterations limit if set */
else if ((__sync_fetch_and_add(&hfuzz->mutationsCnt, 1UL) >= hfuzz->mutationsMax)
&& hfuzz->mutationsMax) {
__sync_fetch_and_add(&hfuzz->threadsFinished, 1UL);
// Wake-up the main process
pthread_kill(fuzz_mainThread, SIGALRM);
return NULL;
}
fuzz_fuzzLoop(hfuzz);
}
}
static void fuzz_runThread(honggfuzz_t * hfuzz, void *(*thread) (void *))
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, _HF_PTHREAD_STACKSIZE);
pthread_attr_setguardsize(&attr, (size_t) sysconf(_SC_PAGESIZE));
pthread_t t;
if (pthread_create(&t, &attr, thread, (void *)hfuzz) < 0) {
PLOG_F("Couldn't create a new thread");
}
return;
}
bool fuzz_setupTimer(void)
{
struct itimerval it = {
.it_value = {.tv_sec = 0,.tv_usec = 1},
.it_interval = {.tv_sec = 1,.tv_usec = 0},
};
if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
PLOG_E("setitimer(ITIMER_REAL)");
return false;
}
return true;
}
void fuzz_main(honggfuzz_t * hfuzz)
{
fuzz_mainThread = pthread_self();
struct sigaction sa = {
.sa_handler = fuzz_sigHandler,
.sa_flags = 0,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGTERM, &sa, NULL) == -1) {
PLOG_F("sigaction(SIGTERM) failed");
}
if (sigaction(SIGINT, &sa, NULL) == -1) {
PLOG_F("sigaction(SIGINT) failed");
}
if (sigaction(SIGQUIT, &sa, NULL) == -1) {
PLOG_F("sigaction(SIGQUIT) failed");
}
if (sigaction(SIGALRM, &sa, NULL) == -1) {
PLOG_F("sigaction(SIGALRM) failed");
}
if (fuzz_setupTimer() == false) {
LOG_F("fuzz_setupTimer()");
}
if (!arch_archInit(hfuzz)) {
LOG_F("Couldn't prepare arch for fuzzing");
}
for (size_t i = 0; i < hfuzz->threadsMax; i++) {
fuzz_runThread(hfuzz, fuzz_threadNew);
}
for (;;) {
if (hfuzz->useScreen) {
display_display(hfuzz);
}
if (fuzz_sigReceived > 0) {
break;
}
if (__sync_fetch_and_add(&hfuzz->threadsFinished, 0UL) >= hfuzz->threadsMax) {
break;
}
pause();
}
if (fuzz_sigReceived > 0) {
LOG_I("Signal %d (%s) received, terminating", fuzz_sigReceived,
strsignal(fuzz_sigReceived));
}
/* Clean-up global buffers */
free(hfuzz->files);
free(hfuzz->dynamicFileBest);
if (hfuzz->dictionary) {
for (size_t i = 0; i < hfuzz->dictionaryCnt; i++) {
free(hfuzz->dictionary[i]);
}
free(hfuzz->dictionary);
}
if (hfuzz->blacklist) {
free(hfuzz->blacklist);
}
if (hfuzz->sanOpts.asanOpts) {
free(hfuzz->sanOpts.asanOpts);
}
if (hfuzz->sanOpts.ubsanOpts) {
free(hfuzz->sanOpts.ubsanOpts);
}
if (hfuzz->sanOpts.msanOpts) {
free(hfuzz->sanOpts.msanOpts);
}
_exit(EXIT_SUCCESS);
}