"Fossies" - the Fresh Open Source Software Archive

Member "honggfuzz-2.2/input.c" (23 Apr 2020, 24111 Bytes) of package /linux/privat/honggfuzz-2.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "input.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1_vs_2.2.

    1 /*
    2  * honggfuzz - file operations
    3  * -----------------------------------------
    4  *
    5  * Author: Robert Swiecki <swiecki@google.com>
    6  *
    7  * Copyright 2010-2020 by Google Inc. All Rights Reserved.
    8  *
    9  * Licensed under the Apache License, Version 2.0 (the "License"); you may
   10  * not use this file except in compliance with the License. You may obtain
   11  * a copy of the License at
   12  *
   13  * http://www.apache.org/licenses/LICENSE-2.0
   14  *
   15  * Unless required by applicable law or agreed to in writing, software
   16  * distributed under the License is distributed on an "AS IS" BASIS,
   17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   18  * implied. See the License for the specific language governing
   19  * permissions and limitations under the License.
   20  *
   21  */
   22 
   23 #include "input.h"
   24 
   25 #include <dirent.h>
   26 #include <errno.h>
   27 #include <fcntl.h>
   28 #include <inttypes.h>
   29 #include <stdint.h>
   30 #include <stdio.h>
   31 #include <stdlib.h>
   32 #include <string.h>
   33 #include <sys/mman.h>
   34 #include <sys/socket.h>
   35 #include <sys/stat.h>
   36 #include <sys/types.h>
   37 #include <unistd.h>
   38 
   39 #include "fuzz.h"
   40 #include "libhfcommon/common.h"
   41 #include "libhfcommon/files.h"
   42 #include "libhfcommon/log.h"
   43 #include "libhfcommon/util.h"
   44 #include "mangle.h"
   45 #include "subproc.h"
   46 
   47 void input_setSize(run_t* run, size_t sz) {
   48     if (run->dynfile->size == sz) {
   49         return;
   50     }
   51     if (sz > run->global->mutate.maxInputSz) {
   52         PLOG_F("Too large size requested: %zu > maxSize: %zu", sz, run->global->mutate.maxInputSz);
   53     }
   54     /* ftruncate of a mmaped file fails under CygWin, it's also painfully slow under MacOS X */
   55 #if !defined(__CYGWIN__) && !defined(_HF_ARCH_DARWIN)
   56     if (TEMP_FAILURE_RETRY(ftruncate(run->dynfile->fd, sz)) == -1) {
   57         PLOG_W("ftruncate(run->dynfile->fd=%d, sz=%zu)", run->dynfile->fd, sz);
   58     }
   59 #endif /* !defined(__CYGWIN__) && !defined(_HF_ARCH_DARWIN) */
   60     run->dynfile->size = sz;
   61 }
   62 
   63 bool input_getDirStatsAndRewind(honggfuzz_t* hfuzz) {
   64     rewinddir(hfuzz->io.inputDirPtr);
   65 
   66     size_t fileCnt = 0U;
   67     for (;;) {
   68         errno = 0;
   69         struct dirent* entry = readdir(hfuzz->io.inputDirPtr);
   70         if (entry == NULL && errno == EINTR) {
   71             continue;
   72         }
   73         if (entry == NULL && errno != 0) {
   74             PLOG_W("readdir('%s')", hfuzz->io.inputDir);
   75             return false;
   76         }
   77         if (entry == NULL) {
   78             break;
   79         }
   80 
   81         char path[PATH_MAX];
   82         snprintf(path, sizeof(path), "%s/%s", hfuzz->io.inputDir, entry->d_name);
   83 
   84         LOG_D("Analyzing file '%s'", path);
   85 
   86         struct stat st;
   87         if (stat(path, &st) == -1) {
   88             LOG_W("Couldn't stat() the '%s' file", path);
   89             continue;
   90         }
   91         if (!S_ISREG(st.st_mode)) {
   92             LOG_D("'%s' is not a regular file, skipping", path);
   93             continue;
   94         }
   95         if (hfuzz->io.maxFileSz && st.st_size > (off_t)hfuzz->io.maxFileSz) {
   96             LOG_D("File '%s' is bigger than maximal defined file size (-F): %" PRIu64 " > %zu",
   97                 path, (uint64_t)st.st_size, hfuzz->io.maxFileSz);
   98         }
   99         if ((size_t)st.st_size > hfuzz->mutate.maxInputSz) {
  100             hfuzz->mutate.maxInputSz = st.st_size;
  101         }
  102         fileCnt++;
  103     }
  104 
  105     ATOMIC_SET(hfuzz->io.fileCnt, fileCnt);
  106     if (hfuzz->io.maxFileSz) {
  107         hfuzz->mutate.maxInputSz = hfuzz->io.maxFileSz;
  108     } else if (hfuzz->mutate.maxInputSz < _HF_INPUT_DEFAULT_SIZE) {
  109         hfuzz->mutate.maxInputSz = _HF_INPUT_DEFAULT_SIZE;
  110     } else if (hfuzz->mutate.maxInputSz > _HF_INPUT_MAX_SIZE) {
  111         hfuzz->mutate.maxInputSz = _HF_INPUT_MAX_SIZE;
  112     }
  113 
  114     if (hfuzz->io.fileCnt == 0U) {
  115         LOG_W("No usable files in the input directory '%s'", hfuzz->io.inputDir);
  116     }
  117 
  118     LOG_D("Analyzed '%s' directory: maxInputSz:%zu, number of usable files:%zu", hfuzz->io.inputDir,
  119         hfuzz->mutate.maxInputSz, hfuzz->io.fileCnt);
  120 
  121     rewinddir(hfuzz->io.inputDirPtr);
  122 
  123     return true;
  124 }
  125 
  126 bool input_getNext(run_t* run, char fname[PATH_MAX], bool rewind) {
  127     static pthread_mutex_t input_mutex = PTHREAD_MUTEX_INITIALIZER;
  128     MX_SCOPED_LOCK(&input_mutex);
  129 
  130     if (run->global->io.fileCnt == 0U) {
  131         LOG_W("No useful files in the input directory");
  132         return false;
  133     }
  134 
  135     for (;;) {
  136         errno = 0;
  137         struct dirent* entry = readdir(run->global->io.inputDirPtr);
  138         if (entry == NULL && errno == EINTR) {
  139             continue;
  140         }
  141         if (entry == NULL && errno != 0) {
  142             PLOG_W("readdir_r('%s')", run->global->io.inputDir);
  143             return false;
  144         }
  145         if (entry == NULL && rewind == false) {
  146             return false;
  147         }
  148         if (entry == NULL && rewind == true) {
  149             if (input_getDirStatsAndRewind(run->global) == false) {
  150                 LOG_E("input_getDirStatsAndRewind('%s')", run->global->io.inputDir);
  151                 return false;
  152             }
  153             continue;
  154         }
  155         char path[PATH_MAX];
  156         snprintf(path, PATH_MAX, "%s/%s", run->global->io.inputDir, entry->d_name);
  157         struct stat st;
  158         if (stat(path, &st) == -1) {
  159             LOG_W("Couldn't stat() the '%s' file", path);
  160             continue;
  161         }
  162         if (!S_ISREG(st.st_mode)) {
  163             LOG_D("'%s' is not a regular file, skipping", path);
  164             continue;
  165         }
  166 
  167         snprintf(fname, PATH_MAX, "%s", entry->d_name);
  168         return true;
  169     }
  170 }
  171 
  172 bool input_init(honggfuzz_t* hfuzz) {
  173     hfuzz->io.fileCnt = 0U;
  174 
  175     if (!hfuzz->io.inputDir) {
  176         LOG_W("No input file/dir specified");
  177         return false;
  178     }
  179 
  180     int dir_fd = TEMP_FAILURE_RETRY(open(hfuzz->io.inputDir, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
  181     if (dir_fd == -1) {
  182         PLOG_W("open('%s', O_DIRECTORY|O_RDONLY|O_CLOEXEC)", hfuzz->io.inputDir);
  183         return false;
  184     }
  185     if ((hfuzz->io.inputDirPtr = fdopendir(dir_fd)) == NULL) {
  186         PLOG_W("fdopendir(dir='%s', fd=%d)", hfuzz->io.inputDir, dir_fd);
  187         close(dir_fd);
  188         return false;
  189     }
  190     if (input_getDirStatsAndRewind(hfuzz) == false) {
  191         hfuzz->io.fileCnt = 0U;
  192         LOG_W("input_getDirStatsAndRewind('%s')", hfuzz->io.inputDir);
  193         return false;
  194     }
  195 
  196     return true;
  197 }
  198 
  199 bool input_parseDictionary(honggfuzz_t* hfuzz) {
  200     LOG_I("Parsing dictionary file '%s'", hfuzz->mutate.dictionaryFile);
  201 
  202     FILE* fDict = fopen(hfuzz->mutate.dictionaryFile, "rb");
  203     if (fDict == NULL) {
  204         PLOG_W("Couldn't open '%s' - R/O mode", hfuzz->mutate.dictionaryFile);
  205         return false;
  206     }
  207     defer {
  208         fclose(fDict);
  209     };
  210 
  211     char* lineptr = NULL;
  212     size_t n = 0;
  213     defer {
  214         free(lineptr);
  215     };
  216     for (;;) {
  217         ssize_t len = getdelim(&lineptr, &n, '\n', fDict);
  218         if (len == -1) {
  219             break;
  220         }
  221         if (hfuzz->mutate.dictionaryCnt == ARRAYSIZE(hfuzz->mutate.dictionary)) {
  222             LOG_W("Maximum number of dictionary entries '%zu' alread loaded. Skipping the rest",
  223                 ARRAYSIZE(hfuzz->mutate.dictionary));
  224             break;
  225         }
  226         if (len > 1 && lineptr[len - 1] == '\n') {
  227             lineptr[len - 1] = '\0';
  228             len--;
  229         }
  230         if (lineptr[0] == '#') {
  231             continue;
  232         }
  233         if (lineptr[0] == '\n') {
  234             continue;
  235         }
  236         if (lineptr[0] == '\0') {
  237             continue;
  238         }
  239 
  240         const char* start = strchr(lineptr, '"');
  241         char* end = strrchr(lineptr, '"');
  242         if (!start || !end) {
  243             LOG_W("Malformed dictionary line '%s', skipping", lineptr);
  244             continue;
  245         }
  246         if ((uintptr_t)start == (uintptr_t)end) {
  247             LOG_W("Malformed dictionary line '%s', skipping", lineptr);
  248             continue;
  249         }
  250         *end = '\0';
  251 
  252         char bufv[1025] = {};
  253         if (sscanf(&start[1], "%1024c", bufv) != 1) {
  254             LOG_W("Malformed dictionary line '%s', skipping", lineptr);
  255             continue;
  256         }
  257 
  258         LOG_D("Parsing dictionary word: '%s'", bufv);
  259 
  260         len = util_decodeCString(bufv);
  261         size_t dictEntry = ATOMIC_POST_INC(hfuzz->mutate.dictionaryCnt);
  262         len = HF_MIN((size_t)len, sizeof(hfuzz->mutate.dictionary[dictEntry].val));
  263         memcpy(hfuzz->mutate.dictionary[dictEntry].val, bufv, len);
  264         hfuzz->mutate.dictionary[dictEntry].len = len;
  265 
  266         LOG_D("Dictionary: loaded word: '%s' (len=%zd)", bufv, len);
  267     }
  268     LOG_I("Loaded %zu words from the dictionary '%s'", hfuzz->mutate.dictionaryCnt,
  269         hfuzz->mutate.dictionaryFile);
  270     return true;
  271 }
  272 
  273 bool input_parseBlacklist(honggfuzz_t* hfuzz) {
  274     FILE* fBl = fopen(hfuzz->feedback.blacklistFile, "rb");
  275     if (fBl == NULL) {
  276         PLOG_W("Couldn't open '%s' - R/O mode", hfuzz->feedback.blacklistFile);
  277         return false;
  278     }
  279     defer {
  280         fclose(fBl);
  281     };
  282 
  283     char* lineptr = NULL;
  284     /* lineptr can be NULL, but it's fine for free() */
  285     defer {
  286         free(lineptr);
  287     };
  288     size_t n = 0;
  289     for (;;) {
  290         if (getline(&lineptr, &n, fBl) == -1) {
  291             break;
  292         }
  293 
  294         if ((hfuzz->feedback.blacklist = util_Realloc(hfuzz->feedback.blacklist,
  295                  (hfuzz->feedback.blacklistCnt + 1) * sizeof(hfuzz->feedback.blacklist[0]))) ==
  296             NULL) {
  297             PLOG_W("realloc failed (sz=%zu)",
  298                 (hfuzz->feedback.blacklistCnt + 1) * sizeof(hfuzz->feedback.blacklist[0]));
  299             return false;
  300         }
  301 
  302         hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt] = strtoull(lineptr, 0, 16);
  303         LOG_D("Blacklist: loaded %'" PRIu64 "'",
  304             hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt]);
  305 
  306         /* Verify entries are sorted so we can use interpolation search */
  307         if (hfuzz->feedback.blacklistCnt > 1) {
  308             if (hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt - 1] >
  309                 hfuzz->feedback.blacklist[hfuzz->feedback.blacklistCnt]) {
  310                 LOG_F("Blacklist file not sorted. Use 'tools/createStackBlacklist.sh' to sort "
  311                       "records");
  312                 return false;
  313             }
  314         }
  315         hfuzz->feedback.blacklistCnt += 1;
  316     }
  317 
  318     if (hfuzz->feedback.blacklistCnt > 0) {
  319         LOG_I("Loaded %zu stack hash(es) from the blacklist file", hfuzz->feedback.blacklistCnt);
  320     } else {
  321         LOG_F("Empty stack hashes blacklist file '%s'", hfuzz->feedback.blacklistFile);
  322     }
  323     return true;
  324 }
  325 
  326 static void input_generateFileName(dynfile_t* dynfile, const char* dir, char fname[PATH_MAX]) {
  327     uint64_t crc64f = util_CRC64(dynfile->data, dynfile->size);
  328     uint64_t crc64r = util_CRC64Rev(dynfile->data, dynfile->size);
  329     if (dir) {
  330         snprintf(fname, PATH_MAX, "%s/%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
  331             dir, crc64f, crc64r, (uint32_t)dynfile->size);
  332     } else {
  333         snprintf(fname, PATH_MAX, "%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
  334             crc64f, crc64r, (uint32_t)dynfile->size);
  335     }
  336 }
  337 
  338 bool input_writeCovFile(const char* dir, dynfile_t* dynfile) {
  339     char fname[PATH_MAX];
  340     input_generateFileName(dynfile, dir, fname);
  341 
  342     if (files_exists(fname)) {
  343         LOG_D("File '%s' already exists in the output corpus directory '%s'", fname, dir);
  344         return true;
  345     }
  346 
  347     LOG_D("Adding file '%s' to the corpus directory '%s'", fname, dir);
  348 
  349     if (!files_writeBufToFile(
  350             fname, dynfile->data, dynfile->size, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC)) {
  351         LOG_W("Couldn't write buffer to file '%s' (sz=%zu)", fname, dynfile->size);
  352         return false;
  353     }
  354 
  355     return true;
  356 }
  357 
  358 /* true if item1 is bigger than item2 */
  359 static bool input_cmpCov(dynfile_t* item1, dynfile_t* item2) {
  360     for (size_t j = 0; j < ARRAYSIZE(item1->cov); j++) {
  361         if (item1->cov[j] > item2->cov[j]) {
  362             return true;
  363         }
  364         if (item1->cov[j] < item2->cov[j]) {
  365             return false;
  366         }
  367     }
  368     /* Both are equal */
  369     return false;
  370 }
  371 
  372 #define TAILQ_FOREACH_HF(var, head, field) \
  373     for ((var) = TAILQ_FIRST((head)); (var); (var) = TAILQ_NEXT((var), field))
  374 
  375 void input_addDynamicInput(run_t* run) {
  376     ATOMIC_SET(run->global->timing.lastCovUpdate, time(NULL));
  377 
  378     dynfile_t* dynfile = (dynfile_t*)util_Calloc(sizeof(dynfile_t));
  379     dynfile->size = run->dynfile->size;
  380     memcpy(dynfile->cov, run->dynfile->cov, sizeof(dynfile->cov));
  381     dynfile->timeExecUSecs = util_timeNowUSecs() - run->timeStartedUSecs;
  382     dynfile->data = (uint8_t*)util_AllocCopy(run->dynfile->data, run->dynfile->size);
  383     dynfile->src = run->dynfile->src;
  384     if (run->dynfile->src) {
  385         ATOMIC_POST_INC(run->dynfile->src->refs);
  386     }
  387     input_generateFileName(dynfile, NULL, dynfile->path);
  388 
  389     MX_SCOPED_RWLOCK_WRITE(&run->global->io.dynfileq_mutex);
  390 
  391     dynfile->idx = ATOMIC_PRE_INC(run->global->io.dynfileqCnt);
  392 
  393     run->global->feedback.maxCov[0] = HF_MAX(run->global->feedback.maxCov[0], dynfile->cov[0]);
  394     run->global->feedback.maxCov[1] = HF_MAX(run->global->feedback.maxCov[1], dynfile->cov[1]);
  395     run->global->feedback.maxCov[2] = HF_MAX(run->global->feedback.maxCov[2], dynfile->cov[2]);
  396     run->global->feedback.maxCov[3] = HF_MAX(run->global->feedback.maxCov[3], dynfile->cov[3]);
  397 
  398     run->global->io.dynfileqMaxSz = HF_MAX(run->global->io.dynfileqMaxSz, dynfile->size);
  399 
  400     /* Sort it by coverage - put better coverage earlier in the list */
  401     dynfile_t* iter = NULL;
  402     TAILQ_FOREACH_HF(iter, &run->global->io.dynfileq, pointers) {
  403         if (input_cmpCov(dynfile, iter)) {
  404             TAILQ_INSERT_BEFORE(iter, dynfile, pointers);
  405             break;
  406         }
  407     }
  408     if (iter == NULL) {
  409         TAILQ_INSERT_TAIL(&run->global->io.dynfileq, dynfile, pointers);
  410     }
  411 
  412     if (run->global->socketFuzzer.enabled) {
  413         /* Don't add coverage data to files in socketFuzzer mode */
  414         return;
  415     }
  416 
  417     const char* outDir =
  418         run->global->io.outputDir ? run->global->io.outputDir : run->global->io.inputDir;
  419     if (!input_writeCovFile(outDir, dynfile)) {
  420         LOG_E("Couldn't save the coverage data to '%s'", run->global->io.outputDir);
  421     }
  422 
  423     /* No need to add files to the new coverage dir, if it's not the main phase */
  424     if (fuzz_getState(run->global) != _HF_STATE_DYNAMIC_MAIN) {
  425         return;
  426     }
  427 
  428     ATOMIC_POST_INC(run->global->io.newUnitsAdded);
  429 
  430     if (run->global->io.covDirNew && !input_writeCovFile(run->global->io.covDirNew, dynfile)) {
  431         LOG_E("Couldn't save the new coverage data to '%s'", run->global->io.covDirNew);
  432     }
  433 }
  434 
  435 bool input_inDynamicCorpus(run_t* run, const char* fname) {
  436     MX_SCOPED_RWLOCK_WRITE(&run->global->io.dynfileq_mutex);
  437 
  438     dynfile_t* iter = NULL;
  439     TAILQ_FOREACH_HF(iter, &run->global->io.dynfileq, pointers) {
  440         if (strncmp(iter->path, fname, PATH_MAX) == 0) {
  441             return true;
  442         }
  443     }
  444     return false;
  445 }
  446 
  447 static inline int input_speedFactor(run_t* run, dynfile_t* dynfile) {
  448     /* Slower the input, lower the chance of it being tested */
  449     uint64_t avg_usecs_per_input =
  450         ((uint64_t)(time(NULL) - run->global->timing.timeStart) * 1000000);
  451     avg_usecs_per_input /= ATOMIC_GET(run->global->cnts.mutationsCnt);
  452     avg_usecs_per_input /= run->global->threads.threadsMax;
  453 
  454     /* Cap both vals to 1us-1s */
  455     avg_usecs_per_input = HF_CAP(avg_usecs_per_input, 1U, 1000000U);
  456     uint64_t sample_usecs = HF_CAP(dynfile->timeExecUSecs, 1U, 1000000U);
  457 
  458     if (sample_usecs >= avg_usecs_per_input) {
  459         return (int)(sample_usecs / avg_usecs_per_input);
  460     } else {
  461         return -(int)(avg_usecs_per_input / sample_usecs);
  462     }
  463 }
  464 
  465 static inline int input_skipFactor(run_t* run, dynfile_t* dynfile, int* speed_factor) {
  466     int penalty = 0;
  467 
  468     {
  469         *speed_factor = HF_CAP(input_speedFactor(run, dynfile) / 2, -15, 15);
  470         penalty += *speed_factor;
  471     }
  472 
  473     {
  474         /* Inputs with lower total coverage -> lower chance of being tested */
  475         static const int scaleMap[200] = {
  476             [95 ... 199] = -15,
  477             [90 ... 94] = -7,
  478             [80 ... 89] = -3,
  479             [60 ... 79] = -1,
  480             [50 ... 59] = 0,
  481             [30 ... 49] = 5,
  482             [11 ... 29] = 10,
  483             [0 ... 10] = 15,
  484         };
  485 
  486         uint64_t maxCov0 = ATOMIC_GET(run->global->feedback.maxCov[0]);
  487         if (maxCov0) {
  488             const unsigned percentile = (dynfile->cov[0] * 100) / maxCov0;
  489             penalty += scaleMap[percentile];
  490         }
  491     }
  492 
  493     {
  494         /* Older inputs -> lower chance of being tested */
  495         static const int scaleMap[200] = {
  496             [100 ... 199] = -10,
  497             [95 ... 99] = -5,
  498             [91 ... 94] = -1,
  499             [81 ... 90] = 0,
  500             [71 ... 80] = 1,
  501             [41 ... 70] = 2,
  502             [0 ... 40] = 3,
  503         };
  504 
  505         const unsigned percentile = (dynfile->idx * 100) / run->global->io.dynfileqCnt;
  506         penalty += scaleMap[percentile];
  507     }
  508 
  509     {
  510         /* If the input wasn't source of other inputs so far, make it less likely to be tested */
  511         penalty += HF_CAP((1 - (int)dynfile->refs) * 3, -30, 5);
  512     }
  513 
  514     {
  515         /* Add penalty for the input being too big - 0 is for 1kB inputs */
  516         if (dynfile->size > 0) {
  517             penalty += HF_CAP(((int)util_Log2(dynfile->size) - 10), -5, 5);
  518         }
  519     }
  520 
  521     return penalty;
  522 }
  523 
  524 bool input_prepareDynamicInput(run_t* run, bool needs_mangle) {
  525     if (ATOMIC_GET(run->global->io.dynfileqCnt) == 0) {
  526         LOG_F("The dynamic file corpus is empty. This shouldn't happen");
  527     }
  528 
  529     int speed_factor = 0;
  530     for (;;) {
  531         MX_SCOPED_RWLOCK_WRITE(&run->global->io.dynfileq_mutex);
  532 
  533         if (run->global->io.dynfileqCurrent == NULL) {
  534             run->global->io.dynfileqCurrent = TAILQ_FIRST(&run->global->io.dynfileq);
  535         }
  536 
  537         if (run->triesLeft) {
  538             run->triesLeft--;
  539             break;
  540         }
  541 
  542         run->current = run->global->io.dynfileqCurrent;
  543         run->global->io.dynfileqCurrent = TAILQ_NEXT(run->global->io.dynfileqCurrent, pointers);
  544 
  545         int skip_factor = input_skipFactor(run, run->current, &speed_factor);
  546         if (skip_factor <= 0) {
  547             run->triesLeft = -(skip_factor);
  548             break;
  549         }
  550 
  551         if ((util_rnd64() % skip_factor) == 0) {
  552             break;
  553         }
  554     }
  555 
  556     input_setSize(run, run->current->size);
  557     memcpy(run->dynfile->cov, run->current->cov, sizeof(run->dynfile->cov));
  558     run->dynfile->idx = run->current->idx;
  559     run->dynfile->timeExecUSecs = run->current->timeExecUSecs;
  560     snprintf(run->dynfile->path, sizeof(run->dynfile->path), "%s", run->current->path);
  561     run->dynfile->src = run->current;
  562     run->dynfile->refs = 0;
  563     memcpy(run->dynfile->data, run->current->data, run->current->size);
  564 
  565     if (needs_mangle) {
  566         mangle_mangleContent(run, speed_factor);
  567     }
  568 
  569     return true;
  570 }
  571 
  572 size_t input_getRandomInputAsBuf(run_t* run, const uint8_t** buf) {
  573     if (ATOMIC_GET(run->global->io.dynfileqCnt) == 0) {
  574         LOG_E("The dynamic input queue shouldn't be empty");
  575         *buf = NULL;
  576         return 0;
  577     }
  578 
  579     dynfile_t* current = NULL;
  580     {
  581         MX_SCOPED_RWLOCK_WRITE(&run->global->io.dynfileq_mutex);
  582 
  583         if (run->global->io.dynfileq2Current == NULL) {
  584             run->global->io.dynfileq2Current = TAILQ_FIRST(&run->global->io.dynfileq);
  585         }
  586 
  587         current = run->global->io.dynfileq2Current;
  588         run->global->io.dynfileq2Current = TAILQ_NEXT(run->global->io.dynfileq2Current, pointers);
  589     }
  590 
  591     *buf = current->data;
  592     return current->size;
  593 }
  594 
  595 static bool input_shouldReadNewFile(run_t* run) {
  596     if (fuzz_getState(run->global) != _HF_STATE_DYNAMIC_DRY_RUN) {
  597         input_setSize(run, run->global->mutate.maxInputSz);
  598         return true;
  599     }
  600 
  601     if (!run->staticFileTryMore) {
  602         run->staticFileTryMore = true;
  603         /* Start with 4 bytes, increase the size in following iterations */
  604         input_setSize(run, HF_MIN(4U, run->global->mutate.maxInputSz));
  605         return true;
  606     }
  607 
  608     /* Increase size of the current file by a factor of 2, and return it instead of a new file */
  609     size_t newsz = run->dynfile->size * 2;
  610     if (newsz >= run->global->mutate.maxInputSz) {
  611         /* That's the largest size for this specific file that will be ever used */
  612         newsz = run->global->mutate.maxInputSz;
  613         run->staticFileTryMore = false;
  614     }
  615 
  616     input_setSize(run, newsz);
  617     return false;
  618 }
  619 
  620 bool input_prepareStaticFile(run_t* run, bool rewind, bool needs_mangle) {
  621     if (input_shouldReadNewFile(run)) {
  622         for (;;) {
  623             if (!input_getNext(run, run->dynfile->path, /* rewind= */ rewind)) {
  624                 return false;
  625             }
  626             if (!needs_mangle || !input_inDynamicCorpus(run, run->dynfile->path)) {
  627                 LOG_D("Skipping '%s' as it's already in the dynamic corpus", run->dynfile->path);
  628                 break;
  629             }
  630         }
  631         run->global->io.testedFileCnt++;
  632     }
  633 
  634     char path[PATH_MAX];
  635     snprintf(path, sizeof(path), "%s/%s", run->global->io.inputDir, run->dynfile->path);
  636 
  637     ssize_t fileSz = files_readFileToBufMax(path, run->dynfile->data, run->dynfile->size);
  638     if (fileSz < 0) {
  639         LOG_E("Couldn't read contents of '%s'", path);
  640         return false;
  641     }
  642 
  643     if (run->staticFileTryMore && ((size_t)fileSz < run->dynfile->size)) {
  644         /* The file is smaller than the requested size, no need to re-read it anymore */
  645         run->staticFileTryMore = false;
  646     }
  647 
  648     input_setSize(run, fileSz);
  649     memset(run->dynfile->cov, '\0', sizeof(run->dynfile->cov));
  650     run->dynfile->idx = 0;
  651     run->dynfile->src = NULL;
  652     run->dynfile->refs = 0;
  653 
  654     if (needs_mangle) {
  655         mangle_mangleContent(run, /* slow_factor= */ 0);
  656     }
  657 
  658     return true;
  659 }
  660 
  661 bool input_removeStaticFile(const char* dir, const char* name) {
  662     char path[PATH_MAX];
  663     snprintf(path, sizeof(path), "%s/%s", dir, name);
  664     if (unlink(path) == -1 && errno != EEXIST) {
  665         PLOG_E("unlink('%s') failed", path);
  666         return false;
  667     }
  668     return true;
  669 }
  670 
  671 bool input_prepareExternalFile(run_t* run) {
  672     snprintf(run->dynfile->path, sizeof(run->dynfile->path), "[EXTERNAL]");
  673 
  674     int fd = files_writeBufToTmpFile(run->global->io.workDir, (const uint8_t*)"", 0, 0);
  675     if (fd == -1) {
  676         LOG_E("Couldn't write input file to a temporary buffer");
  677         return false;
  678     }
  679     defer {
  680         close(fd);
  681     };
  682 
  683     char fname[PATH_MAX];
  684     snprintf(fname, sizeof(fname), "/dev/fd/%d", fd);
  685 
  686     const char* const argv[] = {run->global->exe.externalCommand, fname, NULL};
  687     if (subproc_System(run, argv) != 0) {
  688         LOG_E("Subprocess '%s' returned abnormally", run->global->exe.externalCommand);
  689         return false;
  690     }
  691     LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
  692 
  693     input_setSize(run, run->global->mutate.maxInputSz);
  694     ssize_t sz = files_readFromFdSeek(fd, run->dynfile->data, run->global->mutate.maxInputSz, 0);
  695     if (sz == -1) {
  696         LOG_E("Couldn't read file from fd=%d", fd);
  697         return false;
  698     }
  699 
  700     input_setSize(run, (size_t)sz);
  701     return true;
  702 }
  703 
  704 bool input_postProcessFile(run_t* run, const char* cmd) {
  705     int fd =
  706         files_writeBufToTmpFile(run->global->io.workDir, run->dynfile->data, run->dynfile->size, 0);
  707     if (fd == -1) {
  708         LOG_E("Couldn't write input file to a temporary buffer");
  709         return false;
  710     }
  711     defer {
  712         close(fd);
  713     };
  714 
  715     char fname[PATH_MAX];
  716     snprintf(fname, sizeof(fname), "/dev/fd/%d", fd);
  717 
  718     const char* const argv[] = {cmd, fname, NULL};
  719     if (subproc_System(run, argv) != 0) {
  720         LOG_E("Subprocess '%s' returned abnormally", cmd);
  721         return false;
  722     }
  723     LOG_D("Subporcess '%s' finished with success", cmd);
  724 
  725     input_setSize(run, run->global->mutate.maxInputSz);
  726     ssize_t sz = files_readFromFdSeek(fd, run->dynfile->data, run->global->mutate.maxInputSz, 0);
  727     if (sz == -1) {
  728         LOG_E("Couldn't read file from fd=%d", fd);
  729         return false;
  730     }
  731 
  732     input_setSize(run, (size_t)sz);
  733 
  734     return true;
  735 }