summaryrefslogtreecommitdiff
path: root/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'input.c')
-rw-r--r--input.c554
1 files changed, 416 insertions, 138 deletions
diff --git a/input.c b/input.c
index 372ee055..513b7ed9 100644
--- a/input.c
+++ b/input.c
@@ -1,11 +1,10 @@
/*
- *
* honggfuzz - file operations
* -----------------------------------------
*
* Author: Robert Swiecki <swiecki@google.com>
*
- * Copyright 2010-2018 by Google Inc. All Rights Reserved.
+ * Copyright 2010-2020 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
@@ -27,51 +26,45 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
+#include <sys/queue.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
+#include "fuzz.h"
#include "libhfcommon/common.h"
#include "libhfcommon/files.h"
-#include "mangle.h"
-#include "subproc.h"
-
-#if defined(_HF_ARCH_LINUX)
-#include <sys/syscall.h>
-#if defined(__NR_memfd_create)
-#include <linux/memfd.h>
-#endif /* defined(__NR_memfd_create) */
-#endif /* defined(_HF_ARCH_LINUX) */
-
#include "libhfcommon/log.h"
#include "libhfcommon/util.h"
+#include "mangle.h"
+#include "subproc.h"
void input_setSize(run_t* run, size_t sz) {
- if (sz > run->global->mutate.maxFileSz) {
- PLOG_F("Too large size requested: %zu > maxSize: %zu", sz, run->global->mutate.maxFileSz);
+ if (run->dynfile->size == sz) {
+ return;
+ }
+ if (sz > run->global->mutate.maxInputSz) {
+ PLOG_F("Too large size requested: %zu > maxSize: %zu", sz, run->global->mutate.maxInputSz);
}
/* ftruncate of a mmaped file fails under CygWin, it's also painfully slow under MacOS X */
#if !defined(__CYGWIN__) && !defined(_HF_ARCH_DARWIN)
- if (TEMP_FAILURE_RETRY(ftruncate(run->dynamicFileFd, sz)) == -1) {
- PLOG_W("ftruncate(run->dynamicFileFd=%d, sz=%zu)", run->dynamicFileFd, sz);
+ if (TEMP_FAILURE_RETRY(ftruncate(run->dynfile->fd, sz)) == -1) {
+ PLOG_W("ftruncate(run->dynfile->fd=%d, sz=%zu)", run->dynfile->fd, sz);
}
#endif /* !defined(__CYGWIN__) && !defined(_HF_ARCH_DARWIN) */
- run->dynamicFileSz = sz;
+ run->dynfile->size = sz;
}
-static bool input_getDirStatsAndRewind(honggfuzz_t* hfuzz) {
+bool input_getDirStatsAndRewind(honggfuzz_t* hfuzz) {
rewinddir(hfuzz->io.inputDirPtr);
- size_t maxSize = 0U;
size_t fileCnt = 0U;
for (;;) {
- errno = 0;
+ errno = 0;
struct dirent* entry = readdir(hfuzz->io.inputDirPtr);
if (entry == NULL && errno == EINTR) {
continue;
@@ -84,55 +77,53 @@ static bool input_getDirStatsAndRewind(honggfuzz_t* hfuzz) {
break;
}
- char fname[PATH_MAX];
- snprintf(fname, sizeof(fname), "%s/%s", hfuzz->io.inputDir, entry->d_name);
- LOG_D("Analyzing file '%s'", fname);
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s", hfuzz->io.inputDir, entry->d_name);
+
+ LOG_D("Analyzing file '%s'", path);
struct stat st;
- if (stat(fname, &st) == -1) {
- LOG_W("Couldn't stat() the '%s' file", fname);
+ if (stat(path, &st) == -1) {
+ LOG_W("Couldn't stat() the '%s' file", path);
continue;
}
if (!S_ISREG(st.st_mode)) {
- LOG_D("'%s' is not a regular file, skipping", fname);
+ LOG_D("'%s' is not a regular file, skipping", path);
continue;
}
- if (hfuzz->mutate.maxFileSz != 0UL && st.st_size > (off_t)hfuzz->mutate.maxFileSz) {
- LOG_D("File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %" PRId64,
- fname, (int64_t)st.st_size, (int64_t)hfuzz->mutate.maxFileSz);
+ if (hfuzz->io.maxFileSz && st.st_size > (off_t)hfuzz->io.maxFileSz) {
+ LOG_D("File '%s' is bigger than maximal defined file size (-F): %" PRIu64 " > %zu",
+ path, (uint64_t)st.st_size, hfuzz->io.maxFileSz);
}
- if ((size_t)st.st_size > maxSize) {
- maxSize = st.st_size;
+ if ((size_t)st.st_size > hfuzz->mutate.maxInputSz) {
+ hfuzz->mutate.maxInputSz = st.st_size;
}
fileCnt++;
}
ATOMIC_SET(hfuzz->io.fileCnt, fileCnt);
- if (hfuzz->mutate.maxFileSz == 0U) {
- if (maxSize < 8192) {
- hfuzz->mutate.maxFileSz = 8192;
- } else if (maxSize > _HF_INPUT_MAX_SIZE) {
- hfuzz->mutate.maxFileSz = _HF_INPUT_MAX_SIZE;
- } else {
- hfuzz->mutate.maxFileSz = maxSize;
- }
+ if (hfuzz->io.maxFileSz) {
+ hfuzz->mutate.maxInputSz = hfuzz->io.maxFileSz;
+ } else if (hfuzz->mutate.maxInputSz < _HF_INPUT_DEFAULT_SIZE) {
+ hfuzz->mutate.maxInputSz = _HF_INPUT_DEFAULT_SIZE;
+ } else if (hfuzz->mutate.maxInputSz > _HF_INPUT_MAX_SIZE) {
+ hfuzz->mutate.maxInputSz = _HF_INPUT_MAX_SIZE;
}
if (hfuzz->io.fileCnt == 0U) {
LOG_W("No usable files in the input directory '%s'", hfuzz->io.inputDir);
}
- LOG_D("Re-read the '%s', maxFileSz:%zu, number of usable files:%zu", hfuzz->io.inputDir,
- hfuzz->mutate.maxFileSz, hfuzz->io.fileCnt);
+ LOG_D("Analyzed '%s' directory: maxInputSz:%zu, number of usable files:%zu", hfuzz->io.inputDir,
+ hfuzz->mutate.maxInputSz, hfuzz->io.fileCnt);
rewinddir(hfuzz->io.inputDirPtr);
return true;
}
-bool input_getNext(run_t* run, char* fname, bool rewind) {
- static pthread_mutex_t input_mutex = PTHREAD_MUTEX_INITIALIZER;
- MX_SCOPED_LOCK(&input_mutex);
+bool input_getNext(run_t* run, char fname[PATH_MAX], bool rewind) {
+ MX_SCOPED_LOCK(&run->global->mutex.input);
if (run->global->io.fileCnt == 0U) {
LOG_W("No useful files in the input directory");
@@ -140,7 +131,7 @@ bool input_getNext(run_t* run, char* fname, bool rewind) {
}
for (;;) {
- errno = 0;
+ errno = 0;
struct dirent* entry = readdir(run->global->io.inputDirPtr);
if (entry == NULL && errno == EINTR) {
continue;
@@ -149,28 +140,29 @@ bool input_getNext(run_t* run, char* fname, bool rewind) {
PLOG_W("readdir_r('%s')", run->global->io.inputDir);
return false;
}
- if (entry == NULL && rewind == false) {
+ if (entry == NULL && !rewind) {
return false;
}
- if (entry == NULL && rewind == true) {
- if (input_getDirStatsAndRewind(run->global) == false) {
+ if (entry == NULL && rewind) {
+ if (!input_getDirStatsAndRewind(run->global)) {
LOG_E("input_getDirStatsAndRewind('%s')", run->global->io.inputDir);
return false;
}
continue;
}
-
- snprintf(fname, PATH_MAX, "%s/%s", run->global->io.inputDir, entry->d_name);
-
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "%s/%s", run->global->io.inputDir, entry->d_name);
struct stat st;
- if (stat(fname, &st) == -1) {
- LOG_W("Couldn't stat() the '%s' file", fname);
+ if (stat(path, &st) == -1) {
+ LOG_W("Couldn't stat() the '%s' file", path);
continue;
}
if (!S_ISREG(st.st_mode)) {
- LOG_D("'%s' is not a regular file, skipping", fname);
+ LOG_D("'%s' is not a regular file, skipping", path);
continue;
}
+
+ snprintf(fname, PATH_MAX, "%s", entry->d_name);
return true;
}
}
@@ -189,11 +181,11 @@ bool input_init(honggfuzz_t* hfuzz) {
return false;
}
if ((hfuzz->io.inputDirPtr = fdopendir(dir_fd)) == NULL) {
+ PLOG_W("fdopendir(dir='%s', fd=%d)", hfuzz->io.inputDir, dir_fd);
close(dir_fd);
- PLOG_W("opendir('%s')", hfuzz->io.inputDir);
return false;
}
- if (input_getDirStatsAndRewind(hfuzz) == false) {
+ if (!input_getDirStatsAndRewind(hfuzz)) {
hfuzz->io.fileCnt = 0U;
LOG_W("input_getDirStatsAndRewind('%s')", hfuzz->io.inputDir);
return false;
@@ -203,6 +195,8 @@ bool input_init(honggfuzz_t* hfuzz) {
}
bool input_parseDictionary(honggfuzz_t* hfuzz) {
+ LOG_I("Parsing dictionary file '%s'", hfuzz->mutate.dictionaryFile);
+
FILE* fDict = fopen(hfuzz->mutate.dictionaryFile, "rb");
if (fDict == NULL) {
PLOG_W("Couldn't open '%s' - R/O mode", hfuzz->mutate.dictionaryFile);
@@ -212,8 +206,8 @@ bool input_parseDictionary(honggfuzz_t* hfuzz) {
fclose(fDict);
};
- char* lineptr = NULL;
- size_t n = 0;
+ char* lineptr = NULL;
+ size_t n = 0;
defer {
free(lineptr);
};
@@ -222,6 +216,11 @@ bool input_parseDictionary(honggfuzz_t* hfuzz) {
if (len == -1) {
break;
}
+ if (hfuzz->mutate.dictionaryCnt == ARRAYSIZE(hfuzz->mutate.dictionary)) {
+ LOG_W("Maximum number of dictionary entries '%zu' alread loaded. Skipping the rest",
+ ARRAYSIZE(hfuzz->mutate.dictionary));
+ break;
+ }
if (len > 1 && lineptr[len - 1] == '\n') {
lineptr[len - 1] = '\0';
len--;
@@ -235,26 +234,37 @@ bool input_parseDictionary(honggfuzz_t* hfuzz) {
if (lineptr[0] == '\0') {
continue;
}
- char bufn[1025] = {};
+
+ const char* start = strchr(lineptr, '"');
+ char* end = strrchr(lineptr, '"');
+ if (!start || !end) {
+ LOG_W("Malformed dictionary line '%s', skipping", lineptr);
+ continue;
+ }
+ if ((uintptr_t)start == (uintptr_t)end) {
+ LOG_W("Malformed dictionary line '%s', skipping", lineptr);
+ continue;
+ }
+ *end = '\0';
+
char bufv[1025] = {};
- if (sscanf(lineptr, "\"%1024s", bufv) != 1 &&
- sscanf(lineptr, "%1024[^=]=\"%1024s", bufn, bufv) != 2) {
- LOG_W("Incorrect dictionary entry: '%s'. Skipping", lineptr);
+ if (sscanf(&start[1], "%1024c", bufv) != 1) {
+ LOG_W("Malformed dictionary line '%s', skipping", lineptr);
continue;
}
- LOG_D("Parsing word: '%s'", bufv);
+ LOG_D("Parsing dictionary word: '%s'", bufv);
- char* s = util_StrDup(bufv);
- struct strings_t* str = (struct strings_t*)util_Malloc(sizeof(struct strings_t));
- str->len = util_decodeCString(s);
- str->s = s;
- hfuzz->mutate.dictionaryCnt += 1;
- TAILQ_INSERT_TAIL(&hfuzz->mutate.dictq, str, pointers);
+ len = util_decodeCString(bufv);
+ size_t dictEntry = ATOMIC_POST_INC(hfuzz->mutate.dictionaryCnt);
+ len = HF_MIN((size_t)len, sizeof(hfuzz->mutate.dictionary[dictEntry].val));
+ memcpy(hfuzz->mutate.dictionary[dictEntry].val, bufv, len);
+ hfuzz->mutate.dictionary[dictEntry].len = len;
- LOG_D("Dictionary: loaded word: '%s' (len=%zu)", str->s, str->len);
+ LOG_D("Dictionary: loaded word: '%s' (len=%zd)", bufv, len);
}
- LOG_I("Loaded %zu words from the dictionary", hfuzz->mutate.dictionaryCnt);
+ LOG_I("Loaded %zu words from the dictionary '%s'", hfuzz->mutate.dictionaryCnt,
+ hfuzz->mutate.dictionaryFile);
return true;
}
@@ -292,7 +302,7 @@ bool input_parseBlacklist(honggfuzz_t* hfuzz) {
hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt]);
/* Verify entries are sorted so we can use interpolation search */
- if (hfuzz->feedback.blacklistCnt > 1) {
+ if (hfuzz->feedback.blacklistCnt >= 1) {
if (hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt - 1] >
hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt]) {
LOG_F("Blacklist file not sorted. Use 'tools/createStackBlacklist.sh' to sort "
@@ -311,88 +321,355 @@ bool input_parseBlacklist(honggfuzz_t* hfuzz) {
return true;
}
-bool input_prepareDynamicInput(run_t* run, bool need_mangele) {
+static void input_generateFileName(dynfile_t* dynfile, const char* dir, char fname[PATH_MAX]) {
+ uint64_t crc64f = util_CRC64(dynfile->data, dynfile->size);
+ uint64_t crc64r = util_CRC64Rev(dynfile->data, dynfile->size);
+ if (dir) {
+ snprintf(fname, PATH_MAX, "%s/%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
+ dir, crc64f, crc64r, (uint32_t)dynfile->size);
+ } else {
+ snprintf(fname, PATH_MAX, "%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
+ crc64f, crc64r, (uint32_t)dynfile->size);
+ }
+}
+
+bool input_writeCovFile(const char* dir, dynfile_t* dynfile) {
+ char fname[PATH_MAX];
+ input_generateFileName(dynfile, dir, fname);
+
+ if (files_exists(fname)) {
+ LOG_D("File '%s' already exists in the output corpus directory '%s'", fname, dir);
+ return true;
+ }
+
+ LOG_D("Adding file '%s' to the corpus directory '%s'", fname, dir);
+
+ if (!files_writeBufToFile(
+ fname, dynfile->data, dynfile->size, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC)) {
+ LOG_W("Couldn't write buffer to file '%s' (sz=%zu)", fname, dynfile->size);
+ return false;
+ }
+
+ return true;
+}
+
+/* true if item1 is bigger than item2 */
+static bool input_cmpCov(dynfile_t* item1, dynfile_t* item2) {
+ for (size_t j = 0; j < ARRAYSIZE(item1->cov); j++) {
+ if (item1->cov[j] > item2->cov[j]) {
+ return true;
+ }
+ if (item1->cov[j] < item2->cov[j]) {
+ return false;
+ }
+ }
+ /* Both are equal */
+ return false;
+}
+
+#define TAILQ_FOREACH_HF(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); (var); (var) = TAILQ_NEXT((var), field))
+
+void input_addDynamicInput(run_t* run) {
+ ATOMIC_SET(run->global->timing.lastCovUpdate, time(NULL));
+
+ dynfile_t* dynfile = (dynfile_t*)util_Calloc(sizeof(dynfile_t));
+ dynfile->size = run->dynfile->size;
+ dynfile->timeExecUSecs = util_timeNowUSecs() - run->timeStartedUSecs;
+ dynfile->data = (uint8_t*)util_AllocCopy(run->dynfile->data, run->dynfile->size);
+ dynfile->src = run->dynfile->src;
+ memcpy(dynfile->cov, run->dynfile->cov, sizeof(dynfile->cov));
+ if (run->dynfile->src) {
+ ATOMIC_POST_INC(run->dynfile->src->refs);
+ }
+ input_generateFileName(dynfile, NULL, dynfile->path);
+
+ MX_SCOPED_RWLOCK_WRITE(&run->global->mutex.dynfileq);
+
+ dynfile->idx = ATOMIC_PRE_INC(run->global->io.dynfileqCnt);
+
+ run->global->feedback.maxCov[0] = HF_MAX(run->global->feedback.maxCov[0], dynfile->cov[0]);
+ run->global->feedback.maxCov[1] = HF_MAX(run->global->feedback.maxCov[1], dynfile->cov[1]);
+ run->global->feedback.maxCov[2] = HF_MAX(run->global->feedback.maxCov[2], dynfile->cov[2]);
+ run->global->feedback.maxCov[3] = HF_MAX(run->global->feedback.maxCov[3], dynfile->cov[3]);
+
+ run->global->io.dynfileqMaxSz = HF_MAX(run->global->io.dynfileqMaxSz, dynfile->size);
+
+ /* Sort it by coverage - put better coverage earlier in the list */
+ dynfile_t* iter = NULL;
+ TAILQ_FOREACH_HF (iter, &run->global->io.dynfileq, pointers) {
+ if (input_cmpCov(dynfile, iter)) {
+ TAILQ_INSERT_BEFORE(iter, dynfile, pointers);
+ break;
+ }
+ }
+ if (iter == NULL) {
+ TAILQ_INSERT_TAIL(&run->global->io.dynfileq, dynfile, pointers);
+ }
+
+ if (run->global->socketFuzzer.enabled) {
+ /* Don't add coverage data to files in socketFuzzer mode */
+ return;
+ }
+
+ const char* outDir =
+ run->global->io.outputDir ? run->global->io.outputDir : run->global->io.inputDir;
+ if (!input_writeCovFile(outDir, dynfile)) {
+ LOG_E("Couldn't save the coverage data to '%s'", run->global->io.outputDir);
+ }
+
+ /* No need to add files to the new coverage dir, if it's not the main phase */
+ if (fuzz_getState(run->global) != _HF_STATE_DYNAMIC_MAIN) {
+ return;
+ }
+
+ ATOMIC_POST_INC(run->global->io.newUnitsAdded);
+
+ if (run->global->io.covDirNew && !input_writeCovFile(run->global->io.covDirNew, dynfile)) {
+ LOG_E("Couldn't save the new coverage data to '%s'", run->global->io.covDirNew);
+ }
+}
+
+bool input_inDynamicCorpus(run_t* run, const char* fname) {
+ MX_SCOPED_RWLOCK_WRITE(&run->global->mutex.dynfileq);
+
+ dynfile_t* iter = NULL;
+ TAILQ_FOREACH_HF (iter, &run->global->io.dynfileq, pointers) {
+ if (strncmp(iter->path, fname, PATH_MAX) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline int input_speedFactor(run_t* run, dynfile_t* dynfile) {
+ /* Slower the input, lower the chance of it being tested */
+ uint64_t avg_usecs_per_input =
+ ((uint64_t)(time(NULL) - run->global->timing.timeStart) * 1000000);
+ avg_usecs_per_input /= ATOMIC_GET(run->global->cnts.mutationsCnt);
+ avg_usecs_per_input /= run->global->threads.threadsMax;
+
+ /* Cap both vals to 1us-1s */
+ avg_usecs_per_input = HF_CAP(avg_usecs_per_input, 1U, 1000000U);
+ uint64_t sample_usecs = HF_CAP(dynfile->timeExecUSecs, 1U, 1000000U);
+
+ if (sample_usecs >= avg_usecs_per_input) {
+ return (int)(sample_usecs / avg_usecs_per_input);
+ } else {
+ return -(int)(avg_usecs_per_input / sample_usecs);
+ }
+}
+
+static inline int input_skipFactor(run_t* run, dynfile_t* dynfile, int* speed_factor) {
+ int penalty = 0;
+
{
- MX_SCOPED_RWLOCK_READ(&run->global->io.dynfileq_mutex);
+ *speed_factor = HF_CAP(input_speedFactor(run, dynfile) / 2, -15, 15);
+ penalty += *speed_factor;
+ }
- if (run->global->io.dynfileqCnt == 0) {
- LOG_F("The dynamic file corpus is empty. This shouldn't happen");
+ {
+ /* Inputs with lower total coverage -> lower chance of being tested */
+ static const int scaleMap[200] = {
+ [95 ... 199] = -15,
+ [90 ... 94] = -7,
+ [80 ... 89] = -3,
+ [60 ... 79] = -1,
+ [50 ... 59] = 0,
+ [30 ... 49] = 5,
+ [11 ... 29] = 10,
+ [0 ... 10] = 15,
+ };
+
+ uint64_t maxCov0 = ATOMIC_GET(run->global->feedback.maxCov[0]);
+ if (maxCov0) {
+ const unsigned percentile = (dynfile->cov[0] * 100) / maxCov0;
+ penalty += scaleMap[percentile];
}
+ }
- if (run->dynfileqCurrent == NULL) {
- run->dynfileqCurrent = TAILQ_FIRST(&run->global->io.dynfileq);
- } else {
- if (run->dynfileqCurrent == TAILQ_LAST(&run->global->io.dynfileq, dyns_t)) {
- run->dynfileqCurrent = TAILQ_FIRST(&run->global->io.dynfileq);
- } else {
- run->dynfileqCurrent = TAILQ_NEXT(run->dynfileqCurrent, pointers);
- }
+ {
+ /* Older inputs -> lower chance of being tested */
+ static const int scaleMap[200] = {
+ [100 ... 199] = -10,
+ [95 ... 99] = -5,
+ [91 ... 94] = -1,
+ [81 ... 90] = 0,
+ [71 ... 80] = 1,
+ [41 ... 70] = 2,
+ [0 ... 40] = 3,
+ };
+
+ const unsigned percentile = (dynfile->idx * 100) / run->global->io.dynfileqCnt;
+ penalty += scaleMap[percentile];
+ }
+
+ {
+ /* If the input wasn't source of other inputs so far, make it less likely to be tested */
+ penalty += HF_CAP((1 - (int)dynfile->refs) * 3, -30, 5);
+ }
+
+ {
+ /* Add penalty for the input being too big - 0 is for 1kB inputs */
+ if (dynfile->size > 0) {
+ penalty += HF_CAP(((int)util_Log2(dynfile->size) - 10), -5, 5);
}
}
- input_setSize(run, run->dynfileqCurrent->size);
- memcpy(run->dynamicFile, run->dynfileqCurrent->data, run->dynfileqCurrent->size);
- if (need_mangele) mangle_mangleContent(run);
+ return penalty;
+}
+
+bool input_prepareDynamicInput(run_t* run, bool needs_mangle) {
+ if (ATOMIC_GET(run->global->io.dynfileqCnt) == 0) {
+ LOG_F("The dynamic file corpus is empty. This shouldn't happen");
+ }
+
+ int speed_factor = 0;
+ for (;;) {
+ MX_SCOPED_RWLOCK_WRITE(&run->global->mutex.dynfileq);
+
+ if (run->global->io.dynfileqCurrent == NULL) {
+ run->global->io.dynfileqCurrent = TAILQ_FIRST(&run->global->io.dynfileq);
+ }
+
+ if (run->triesLeft) {
+ run->triesLeft--;
+ break;
+ }
+
+ run->current = run->global->io.dynfileqCurrent;
+ run->global->io.dynfileqCurrent = TAILQ_NEXT(run->global->io.dynfileqCurrent, pointers);
+
+ int skip_factor = input_skipFactor(run, run->current, &speed_factor);
+ if (skip_factor <= 0) {
+ run->triesLeft = -(skip_factor);
+ break;
+ }
+
+ if ((util_rnd64() % skip_factor) == 0) {
+ break;
+ }
+ }
+
+ input_setSize(run, run->current->size);
+ run->dynfile->idx = run->current->idx;
+ run->dynfile->timeExecUSecs = run->current->timeExecUSecs;
+ run->dynfile->src = run->current;
+ run->dynfile->refs = 0;
+ memcpy(run->dynfile->cov, run->current->cov, sizeof(run->dynfile->cov));
+ snprintf(run->dynfile->path, sizeof(run->dynfile->path), "%s", run->current->path);
+ memcpy(run->dynfile->data, run->current->data, run->current->size);
+
+ if (needs_mangle) {
+ mangle_mangleContent(run, speed_factor);
+ }
return true;
}
-bool input_prepareStaticFile(run_t* run, bool rewind, bool need_mangele) {
- char fname[PATH_MAX];
- if (!input_getNext(run, fname, /* rewind= */ rewind)) {
- return false;
+size_t input_getRandomInputAsBuf(run_t* run, const uint8_t** buf) {
+ if (ATOMIC_GET(run->global->io.dynfileqCnt) == 0) {
+ LOG_E("The dynamic input queue shouldn't be empty");
+ *buf = NULL;
+ return 0;
}
- snprintf(run->origFileName, sizeof(run->origFileName), "%s", fname);
- input_setSize(run, run->global->mutate.maxFileSz);
- ssize_t fileSz = files_readFileToBufMax(fname, run->dynamicFile, run->global->mutate.maxFileSz);
- if (fileSz < 0) {
- LOG_E("Couldn't read contents of '%s'", fname);
- return false;
+ dynfile_t* current = NULL;
+ {
+ MX_SCOPED_RWLOCK_WRITE(&run->global->mutex.dynfileq);
+
+ if (run->global->io.dynfileq2Current == NULL) {
+ run->global->io.dynfileq2Current = TAILQ_FIRST(&run->global->io.dynfileq);
+ }
+
+ current = run->global->io.dynfileq2Current;
+ run->global->io.dynfileq2Current = TAILQ_NEXT(run->global->io.dynfileq2Current, pointers);
}
- input_setSize(run, fileSz);
- if (need_mangele) mangle_mangleContent(run);
+ *buf = current->data;
+ return current->size;
+}
- return true;
+static bool input_shouldReadNewFile(run_t* run) {
+ if (fuzz_getState(run->global) != _HF_STATE_DYNAMIC_DRY_RUN) {
+ input_setSize(run, run->global->mutate.maxInputSz);
+ return true;
+ }
+
+ if (!run->staticFileTryMore) {
+ run->staticFileTryMore = true;
+ /* Start with 4 bytes, increase the size in following iterations */
+ input_setSize(run, HF_MIN(4U, run->global->mutate.maxInputSz));
+ return true;
+ }
+
+ /* Increase size of the current file by a factor of 2, and return it instead of a new file */
+ size_t newsz = run->dynfile->size * 2;
+ if (newsz >= run->global->mutate.maxInputSz) {
+ /* That's the largest size for this specific file that will be ever used */
+ newsz = run->global->mutate.maxInputSz;
+ run->staticFileTryMore = false;
+ }
+
+ input_setSize(run, newsz);
+ return false;
}
-bool input_prepareExternalFile(run_t* run) {
- snprintf(run->origFileName, sizeof(run->origFileName), "[EXTERNAL]");
+bool input_prepareStaticFile(run_t* run, bool rewind, bool needs_mangle) {
+ if (input_shouldReadNewFile(run)) {
+ for (;;) {
+ if (!input_getNext(run, run->dynfile->path, /* rewind= */ rewind)) {
+ return false;
+ }
+ if (!needs_mangle || !input_inDynamicCorpus(run, run->dynfile->path)) {
+ LOG_D("Skipping '%s' as it's already in the dynamic corpus", run->dynfile->path);
+ break;
+ }
+ }
+ run->global->io.testedFileCnt++;
+ }
- int fd = files_writeBufToTmpFile(run->global->io.workDir, (const uint8_t*)"", 0, 0);
- if (fd == -1) {
- LOG_E("Couldn't write input file to a temporary buffer");
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s", run->global->io.inputDir, run->dynfile->path);
+
+ ssize_t fileSz = files_readFileToBufMax(path, run->dynfile->data, run->dynfile->size);
+ if (fileSz < 0) {
+ LOG_E("Couldn't read contents of '%s'", path);
return false;
}
- defer {
- close(fd);
- };
- char fname[PATH_MAX];
- snprintf(fname, sizeof(fname), "/dev/fd/%d", fd);
+ if (run->staticFileTryMore && ((size_t)fileSz < run->dynfile->size)) {
+ /* The file is smaller than the requested size, no need to re-read it anymore */
+ run->staticFileTryMore = false;
+ }
- const char* const argv[] = {run->global->exe.externalCommand, fname, NULL};
- if (subproc_System(run, argv) != 0) {
- LOG_E("Subprocess '%s' returned abnormally", run->global->exe.externalCommand);
- return false;
+ input_setSize(run, fileSz);
+ memset(run->dynfile->cov, '\0', sizeof(run->dynfile->cov));
+ run->dynfile->idx = 0;
+ run->dynfile->src = NULL;
+ run->dynfile->refs = 0;
+
+ if (needs_mangle) {
+ mangle_mangleContent(run, /* slow_factor= */ 0);
}
- LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
- input_setSize(run, run->global->mutate.maxFileSz);
- ssize_t sz = files_readFromFdSeek(fd, run->dynamicFile, run->global->mutate.maxFileSz, 0);
- if (sz == -1) {
- LOG_E("Couldn't read file from fd=%d", fd);
+ return true;
+}
+
+bool input_removeStaticFile(const char* dir, const char* name) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s", dir, name);
+ if (unlink(path) == -1 && errno != EEXIST) {
+ PLOG_E("unlink('%s') failed", path);
return false;
}
-
- input_setSize(run, (size_t)sz);
return true;
}
-bool input_postProcessFile(run_t* run) {
- int fd =
- files_writeBufToTmpFile(run->global->io.workDir, run->dynamicFile, run->dynamicFileSz, 0);
+bool input_prepareExternalFile(run_t* run) {
+ snprintf(run->dynfile->path, sizeof(run->dynfile->path), "[EXTERNAL]");
+
+ int fd = files_writeBufToTmpFile(run->global->io.workDir, (const uint8_t*)"", 0, 0);
if (fd == -1) {
LOG_E("Couldn't write input file to a temporary buffer");
return false;
@@ -404,15 +681,15 @@ bool input_postProcessFile(run_t* run) {
char fname[PATH_MAX];
snprintf(fname, sizeof(fname), "/dev/fd/%d", fd);
- const char* const argv[] = {run->global->exe.postExternalCommand, fname, NULL};
+ const char* const argv[] = {run->global->exe.externalCommand, fname, NULL};
if (subproc_System(run, argv) != 0) {
- LOG_E("Subprocess '%s' returned abnormally", run->global->exe.postExternalCommand);
+ LOG_E("Subprocess '%s' returned abnormally", run->global->exe.externalCommand);
return false;
}
LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
- input_setSize(run, run->global->mutate.maxFileSz);
- ssize_t sz = files_readFromFdSeek(fd, run->dynamicFile, run->global->mutate.maxFileSz, 0);
+ input_setSize(run, run->global->mutate.maxInputSz);
+ ssize_t sz = files_readFromFdSeek(fd, run->dynfile->data, run->global->mutate.maxInputSz, 0);
if (sz == -1) {
LOG_E("Couldn't read file from fd=%d", fd);
return false;
@@ -422,9 +699,9 @@ bool input_postProcessFile(run_t* run) {
return true;
}
-bool input_feedbackMutateFile(run_t* run) {
+bool input_postProcessFile(run_t* run, const char* cmd) {
int fd =
- files_writeBufToTmpFile(run->global->io.workDir, run->dynamicFile, run->dynamicFileSz, 0);
+ files_writeBufToTmpFile(run->global->io.workDir, run->dynfile->data, run->dynfile->size, 0);
if (fd == -1) {
LOG_E("Couldn't write input file to a temporary buffer");
return false;
@@ -436,20 +713,21 @@ bool input_feedbackMutateFile(run_t* run) {
char fname[PATH_MAX];
snprintf(fname, sizeof(fname), "/dev/fd/%d", fd);
- const char* const argv[] = {run->global->exe.feedbackMutateCommand, fname, NULL};
+ const char* const argv[] = {cmd, fname, NULL};
if (subproc_System(run, argv) != 0) {
- LOG_E("Subprocess '%s' returned abnormally", run->global->exe.feedbackMutateCommand);
+ LOG_E("Subprocess '%s' returned abnormally", cmd);
return false;
}
- LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
+ LOG_D("Subporcess '%s' finished with success", cmd);
- input_setSize(run, run->global->mutate.maxFileSz);
- ssize_t sz = files_readFromFdSeek(fd, run->dynamicFile, run->global->mutate.maxFileSz, 0);
+ input_setSize(run, run->global->mutate.maxInputSz);
+ ssize_t sz = files_readFromFdSeek(fd, run->dynfile->data, run->global->mutate.maxInputSz, 0);
if (sz == -1) {
LOG_E("Couldn't read file from fd=%d", fd);
return false;
}
input_setSize(run, (size_t)sz);
+
return true;
}