Home | History | Annotate | Download | only in honggfuzz
      1 /*
      2  *
      3  * honggfuzz - fuzzing routines
      4  * -----------------------------------------
      5  *
      6  * Author:
      7  * Robert Swiecki <swiecki (at) google.com>
      8  * Felix Grbert <groebert (at) google.com>
      9  *
     10  * Copyright 2010-2015 by Google Inc. All Rights Reserved.
     11  *
     12  * Licensed under the Apache License, Version 2.0 (the "License"); you may
     13  * not use this file except in compliance with the License. You may obtain
     14  * a copy of the License at
     15  *
     16  * http://www.apache.org/licenses/LICENSE-2.0
     17  *
     18  * Unless required by applicable law or agreed to in writing, software
     19  * distributed under the License is distributed on an "AS IS" BASIS,
     20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     21  * implied. See the License for the specific language governing
     22  * permissions and limitations under the License.
     23  *
     24  */
     25 
     26 #include "fuzz.h"
     27 
     28 #include <errno.h>
     29 #include <fcntl.h>
     30 #include <inttypes.h>
     31 #include <libgen.h>
     32 #include <pthread.h>
     33 #include <signal.h>
     34 #include <stddef.h>
     35 #include <stdint.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <sys/mman.h>
     40 #include <sys/param.h>
     41 #include <sys/stat.h>
     42 #include <sys/time.h>
     43 #include <sys/types.h>
     44 #include <time.h>
     45 #include <unistd.h>
     46 
     47 #include "arch.h"
     48 #include "honggfuzz.h"
     49 #include "input.h"
     50 #include "libcommon/common.h"
     51 #include "libcommon/files.h"
     52 #include "libcommon/log.h"
     53 #include "libcommon/util.h"
     54 #include "mangle.h"
     55 #include "report.h"
     56 #include "sancov.h"
     57 #include "sanitizers.h"
     58 #include "subproc.h"
     59 
     60 static time_t termTimeStamp = 0;
     61 
     62 bool fuzz_isTerminating(void) {
     63     if (ATOMIC_GET(termTimeStamp) != 0) {
     64         return true;
     65     }
     66     return false;
     67 }
     68 
     69 void fuzz_setTerminating(void) {
     70     if (ATOMIC_GET(termTimeStamp) != 0) {
     71         return;
     72     }
     73     ATOMIC_SET(termTimeStamp, time(NULL));
     74 }
     75 
     76 bool fuzz_shouldTerminate() {
     77     if (ATOMIC_GET(termTimeStamp) == 0) {
     78         return false;
     79     }
     80     if ((time(NULL) - ATOMIC_GET(termTimeStamp)) > 5) {
     81         return true;
     82     }
     83     return false;
     84 }
     85 
     86 static void fuzz_getFileName(run_t* run) {
     87     char bname[PATH_MAX];
     88     snprintf(bname, sizeof(bname), "%s", run->global->exe.cmdline[0]);
     89     snprintf(run->fileName, PATH_MAX, "%s/honggfuzz.input.%" PRIu32 ".%s.%s",
     90         run->global->io.workDir, run->fuzzNo, basename(bname), run->global->io.fileExtn);
     91 }
     92 
     93 static bool fuzz_prepareFileDynamically(run_t* run) {
     94     run->origFileName = "[DYNAMIC]";
     95 
     96     {
     97         MX_SCOPED_RWLOCK_READ(&run->global->dynfileq_mutex);
     98 
     99         if (run->global->dynfileqCnt == 0) {
    100             LOG_F(
    101                 "The dynamic file corpus is empty. Apparently, the initial fuzzing of the "
    102                 "provided file corpus (-f) has not produced any follow-up files with positive "
    103                 "coverage and/or CPU counters");
    104         }
    105 
    106         if (run->dynfileqCurrent == NULL) {
    107             run->dynfileqCurrent = TAILQ_FIRST(&run->global->dynfileq);
    108         } else {
    109             if (run->dynfileqCurrent == TAILQ_LAST(&run->global->dynfileq, dyns_t)) {
    110                 run->dynfileqCurrent = TAILQ_FIRST(&run->global->dynfileq);
    111             } else {
    112                 run->dynfileqCurrent = TAILQ_NEXT(run->dynfileqCurrent, pointers);
    113             }
    114         }
    115     }
    116 
    117     memcpy(run->dynamicFile, run->dynfileqCurrent->data, run->dynfileqCurrent->size);
    118     run->dynamicFileSz = run->dynfileqCurrent->size;
    119 
    120     mangle_mangleContent(run);
    121 
    122     if (run->global->persistent == false &&
    123         files_writeBufToFile(run->fileName, run->dynamicFile, run->dynamicFileSz,
    124             O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC) == false) {
    125         LOG_E("Couldn't write buffer to file '%s'", run->fileName);
    126         return false;
    127     }
    128 
    129     return true;
    130 }
    131 
    132 static bool fuzz_prepareFile(run_t* run, bool rewind) {
    133     char fname[PATH_MAX];
    134     if (input_getNext(run, fname, rewind) == false) {
    135         return false;
    136     }
    137     run->origFileName = files_basename(fname);
    138 
    139     ssize_t fileSz = files_readFileToBufMax(fname, run->dynamicFile, run->global->maxFileSz);
    140     if (fileSz < 0) {
    141         LOG_E("Couldn't read contents of '%s'", fname);
    142         return false;
    143     }
    144     run->dynamicFileSz = fileSz;
    145 
    146     mangle_mangleContent(run);
    147 
    148     if (run->global->persistent == false &&
    149         files_writeBufToFile(run->fileName, run->dynamicFile, run->dynamicFileSz,
    150             O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC) == false) {
    151         LOG_E("Couldn't write buffer to file '%s'", run->fileName);
    152         return false;
    153     }
    154 
    155     return true;
    156 }
    157 
    158 static bool fuzz_prepareFileExternally(run_t* run) {
    159     char fname[PATH_MAX];
    160     if (input_getNext(run, fname, true /* rewind */)) {
    161         run->origFileName = files_basename(fname);
    162         if (files_copyFile(fname, run->fileName, NULL, false /* try_link */) == false) {
    163             LOG_E("files_copyFile('%s', '%s')", fname, run->fileName);
    164             return false;
    165         }
    166     } else {
    167         run->origFileName = "[EXTERNAL]";
    168         int dstfd = open(run->fileName, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644);
    169         if (dstfd == -1) {
    170             PLOG_E("Couldn't create a temporary file '%s'", run->fileName);
    171             return false;
    172         }
    173         close(dstfd);
    174     }
    175 
    176     LOG_D("Created '%s' as an input file", run->fileName);
    177 
    178     const char* const argv[] = {run->global->exe.externalCommand, run->fileName, NULL};
    179     if (subproc_System(run, argv) != 0) {
    180         LOG_E("Subprocess '%s' returned abnormally", run->global->exe.externalCommand);
    181         return false;
    182     }
    183     LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
    184 
    185     ssize_t rsz = files_readFileToBufMax(run->fileName, run->dynamicFile, run->global->maxFileSz);
    186     if (rsz < 0) {
    187         LOG_W("Couldn't read back '%s' to the buffer", run->fileName);
    188         return false;
    189     }
    190     run->dynamicFileSz = rsz;
    191 
    192     if (run->global->persistent) {
    193         unlink(run->fileName);
    194     }
    195 
    196     return true;
    197 }
    198 
    199 static bool fuzz_postProcessFile(run_t* run) {
    200     if (run->global->persistent) {
    201         if (files_writeBufToFile(run->fileName, run->dynamicFile, run->dynamicFileSz,
    202                 O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC) == false) {
    203             LOG_E("Couldn't write file to '%s'", run->fileName);
    204             return false;
    205         }
    206     }
    207 
    208     const char* const argv[] = {run->global->exe.postExternalCommand, run->fileName, NULL};
    209     if (subproc_System(run, argv) != 0) {
    210         LOG_E("Subprocess '%s' returned abnormally", run->global->exe.postExternalCommand);
    211         return false;
    212     }
    213     LOG_D("Subporcess '%s' finished with success", run->global->exe.externalCommand);
    214 
    215     ssize_t rsz = files_readFileToBufMax(run->fileName, run->dynamicFile, run->global->maxFileSz);
    216     if (rsz < 0) {
    217         LOG_W("Couldn't read back '%s' to the buffer", run->fileName);
    218         return false;
    219     }
    220     run->dynamicFileSz = rsz;
    221 
    222     return true;
    223 }
    224 
    225 static fuzzState_t fuzz_getState(honggfuzz_t* hfuzz) { return ATOMIC_GET(hfuzz->state); }
    226 
    227 static void fuzz_setState(honggfuzz_t* hfuzz, fuzzState_t state) {
    228     /* All threads must indicate willingness to switch to _HF_STATE_DYNAMIC_MAIN */
    229     if (state == _HF_STATE_DYNAMIC_MAIN) {
    230         static size_t cnt = 0;
    231         ATOMIC_PRE_INC(cnt);
    232         while (ATOMIC_GET(cnt) < hfuzz->threads.threadsMax) {
    233             if (fuzz_isTerminating()) {
    234                 return;
    235             }
    236             sleep(1);
    237         }
    238     }
    239 
    240     static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
    241     MX_SCOPED_LOCK(&state_mutex);
    242 
    243     if (hfuzz->state == state) {
    244         return;
    245     }
    246 
    247     switch (state) {
    248         case _HF_STATE_DYNAMIC_PRE:
    249             LOG_I("Entering phase 1/2: Dry Run");
    250             break;
    251         case _HF_STATE_DYNAMIC_MAIN:
    252             LOG_I("Entering phase 2/2: Main");
    253             break;
    254         case _HF_STATE_STATIC:
    255             LOG_I("Entering phase: Static");
    256             break;
    257         default:
    258             LOG_I("Entering unknown phase: %d", state);
    259             break;
    260     }
    261 
    262     ATOMIC_SET(hfuzz->state, state);
    263 }
    264 
    265 static bool fuzz_runVerifier(run_t* crashedFuzzer) {
    266     int crashFd = -1;
    267     uint8_t* crashBuf = NULL;
    268     off_t crashFileSz = 0;
    269 
    270     crashBuf = files_mapFile(crashedFuzzer->crashFileName, &crashFileSz, &crashFd, false);
    271     if (crashBuf == NULL) {
    272         LOG_E("Couldn't open and map '%s' in R/O mode", crashedFuzzer->crashFileName);
    273         return false;
    274     }
    275     defer {
    276         munmap(crashBuf, crashFileSz);
    277         close(crashFd);
    278     };
    279 
    280     LOG_I("Launching verifier for %" PRIx64 " hash", crashedFuzzer->backtrace);
    281     for (int i = 0; i < _HF_VERIFIER_ITER; i++) {
    282         run_t vFuzzer = {
    283             .global = crashedFuzzer->global,
    284             .pid = 0,
    285             .persistentPid = 0,
    286             .state = fuzz_getState(crashedFuzzer->global),
    287             .timeStartedMillis = util_timeNowMillis(),
    288             .crashFileName = {0},
    289             .pc = 0ULL,
    290             .backtrace = 0ULL,
    291             .access = 0ULL,
    292             .exception = 0,
    293             .dynfileqCurrent = NULL,
    294             .dynamicFileSz = 0,
    295             .dynamicFile = NULL,
    296             .sanCovCnts =
    297                 {
    298                     .hitBBCnt = 0ULL,
    299                     .totalBBCnt = 0ULL,
    300                     .dsoCnt = 0ULL,
    301                     .iDsoCnt = 0ULL,
    302                     .newBBCnt = 0ULL,
    303                     .crashesCnt = 0ULL,
    304                 },
    305             .report = {'\0'},
    306             .mainWorker = false,
    307             .fuzzNo = crashedFuzzer->fuzzNo,
    308             .persistentSock = -1,
    309             .tmOutSignaled = false,
    310 
    311             .linux =
    312                 {
    313                     .hwCnts =
    314                         {
    315                             .cpuInstrCnt = 0ULL,
    316                             .cpuBranchCnt = 0ULL,
    317                             .bbCnt = 0ULL,
    318                             .newBBCnt = 0ULL,
    319                             .softCntPc = 0ULL,
    320                             .softCntEdge = 0ULL,
    321                             .softCntCmp = 0ULL,
    322                         },
    323                     .attachedPid = 0,
    324                 },
    325         };
    326 
    327         if (arch_archThreadInit(&vFuzzer) == false) {
    328             LOG_F("Could not initialize the thread");
    329         }
    330 
    331         fuzz_getFileName(&vFuzzer);
    332         if (files_writeBufToFile(vFuzzer.fileName, crashBuf, crashFileSz,
    333                 O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC) == false) {
    334             LOG_E("Couldn't write buffer to file '%s'", vFuzzer.fileName);
    335             return false;
    336         }
    337 
    338         if (subproc_Run(&vFuzzer) == false) {
    339             LOG_F("subproc_Run()");
    340         }
    341 
    342         /* Delete intermediate files generated from verifier */
    343         unlink(vFuzzer.fileName);
    344 
    345         /* If stack hash doesn't match skip name tag and exit */
    346         if (crashedFuzzer->backtrace != vFuzzer.backtrace) {
    347             LOG_D("Verifier stack hash mismatch");
    348             return false;
    349         }
    350     }
    351 
    352     /* Workspace is inherited, just append a extra suffix */
    353     char verFile[PATH_MAX] = {0};
    354     snprintf(verFile, sizeof(verFile), "%s.verified", crashedFuzzer->crashFileName);
    355 
    356     /* Copy file with new suffix & remove original copy */
    357     bool dstFileExists = false;
    358     if (files_copyFile(
    359             crashedFuzzer->crashFileName, verFile, &dstFileExists, true /* try_link */)) {
    360         LOG_I("Successfully verified, saving as (%s)", verFile);
    361         ATOMIC_POST_INC(crashedFuzzer->global->cnts.verifiedCrashesCnt);
    362         unlink(crashedFuzzer->crashFileName);
    363     } else {
    364         if (dstFileExists) {
    365             LOG_I("It seems that '%s' already exists, skipping", verFile);
    366         } else {
    367             LOG_E("Couldn't copy '%s' to '%s'", crashedFuzzer->crashFileName, verFile);
    368             return false;
    369         }
    370     }
    371 
    372     return true;
    373 }
    374 
    375 static bool fuzz_writeCovFile(const char* dir, const uint8_t* data, size_t len) {
    376     char fname[PATH_MAX];
    377 
    378     uint64_t crc64f = util_CRC64(data, len);
    379     uint64_t crc64r = util_CRC64Rev(data, len);
    380     snprintf(fname, sizeof(fname), "%s/%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
    381         dir, crc64f, crc64r, (uint32_t)len);
    382 
    383     if (access(fname, R_OK) == 0) {
    384         LOG_D("File '%s' already exists in the output corpus directory '%s'", fname, dir);
    385         return true;
    386     }
    387 
    388     LOG_D("Adding file '%s' to the corpus directory '%s'", fname, dir);
    389 
    390     if (files_writeBufToFile(fname, data, len, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC) ==
    391         false) {
    392         LOG_W("Couldn't write buffer to file '%s'", fname);
    393         return false;
    394     }
    395 
    396     return true;
    397 }
    398 
    399 static void fuzz_addFileToFileQ(run_t* run) {
    400     struct dynfile_t* dynfile = (struct dynfile_t*)util_Malloc(sizeof(struct dynfile_t));
    401     dynfile->size = run->dynamicFileSz;
    402     dynfile->data = (uint8_t*)util_Malloc(run->dynamicFileSz);
    403     memcpy(dynfile->data, run->dynamicFile, run->dynamicFileSz);
    404 
    405     MX_SCOPED_RWLOCK_WRITE(&run->global->dynfileq_mutex);
    406     TAILQ_INSERT_TAIL(&run->global->dynfileq, dynfile, pointers);
    407     run->global->dynfileqCnt++;
    408 
    409     if (!fuzz_writeCovFile(run->global->io.covDirAll, run->dynamicFile, run->dynamicFileSz)) {
    410         LOG_E("Couldn't save the coverage data to '%s'", run->global->io.covDirAll);
    411     }
    412 
    413     /* No need to add files to the new coverage dir, if this is just the dry-run phase */
    414     if (run->state == _HF_STATE_DYNAMIC_PRE || run->global->io.covDirNew == NULL) {
    415         return;
    416     }
    417 
    418     if (!fuzz_writeCovFile(run->global->io.covDirNew, run->dynamicFile, run->dynamicFileSz)) {
    419         LOG_E("Couldn't save the new coverage data to '%s'", run->global->io.covDirNew);
    420     }
    421 }
    422 
    423 static void fuzz_perfFeedback(run_t* run) {
    424     if (run->global->skipFeedbackOnTimeout && run->tmOutSignaled) {
    425         return;
    426     }
    427 
    428     LOG_D("New file size: %zu, Perf feedback new/cur (instr,branch): %" PRIu64 "/%" PRIu64
    429           "/%" PRIu64 "/%" PRIu64 ", BBcnt new/total: %" PRIu64 "/%" PRIu64,
    430         run->dynamicFileSz, run->linux.hwCnts.cpuInstrCnt, run->global->linux.hwCnts.cpuInstrCnt,
    431         run->linux.hwCnts.cpuBranchCnt, run->global->linux.hwCnts.cpuBranchCnt,
    432         run->linux.hwCnts.newBBCnt, run->global->linux.hwCnts.bbCnt);
    433 
    434     MX_SCOPED_LOCK(&run->global->feedback_mutex);
    435 
    436     uint64_t softCntPc = 0UL;
    437     uint64_t softCntEdge = 0UL;
    438     uint64_t softCntCmp = 0UL;
    439     if (run->global->bbFd != -1) {
    440         softCntPc = ATOMIC_GET(run->global->feedback->pidFeedbackPc[run->fuzzNo]);
    441         ATOMIC_CLEAR(run->global->feedback->pidFeedbackPc[run->fuzzNo]);
    442         softCntEdge = ATOMIC_GET(run->global->feedback->pidFeedbackEdge[run->fuzzNo]);
    443         ATOMIC_CLEAR(run->global->feedback->pidFeedbackEdge[run->fuzzNo]);
    444         softCntCmp = ATOMIC_GET(run->global->feedback->pidFeedbackCmp[run->fuzzNo]);
    445         ATOMIC_CLEAR(run->global->feedback->pidFeedbackCmp[run->fuzzNo]);
    446     }
    447 
    448     int64_t diff0 = run->global->linux.hwCnts.cpuInstrCnt - run->linux.hwCnts.cpuInstrCnt;
    449     int64_t diff1 = run->global->linux.hwCnts.cpuBranchCnt - run->linux.hwCnts.cpuBranchCnt;
    450 
    451     /*
    452      * Coverage is the primary counter, the rest is secondary, and taken into consideration only
    453      * if the coverage counter has not been changed
    454      */
    455     if (run->linux.hwCnts.newBBCnt > 0 || softCntPc > 0 || softCntEdge > 0 || softCntCmp > 0 ||
    456         diff0 < 0 || diff1 < 0) {
    457         if (diff0 < 0) {
    458             run->global->linux.hwCnts.cpuInstrCnt = run->linux.hwCnts.cpuInstrCnt;
    459         }
    460         if (diff1 < 0) {
    461             run->global->linux.hwCnts.cpuBranchCnt = run->linux.hwCnts.cpuBranchCnt;
    462         }
    463         run->global->linux.hwCnts.bbCnt += run->linux.hwCnts.newBBCnt;
    464         run->global->linux.hwCnts.softCntPc += softCntPc;
    465         run->global->linux.hwCnts.softCntEdge += softCntEdge;
    466         run->global->linux.hwCnts.softCntCmp += softCntCmp;
    467 
    468         LOG_I("Size:%zu (i,b,hw,edge,ip,cmp): %" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64
    469               "/%" PRIu64 "/%" PRIu64 ", Tot:%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64
    470               "/%" PRIu64 "/%" PRIu64,
    471             run->dynamicFileSz, run->linux.hwCnts.cpuInstrCnt, run->linux.hwCnts.cpuBranchCnt,
    472             run->linux.hwCnts.newBBCnt, softCntEdge, softCntPc, softCntCmp,
    473             run->global->linux.hwCnts.cpuInstrCnt, run->global->linux.hwCnts.cpuBranchCnt,
    474             run->global->linux.hwCnts.bbCnt, run->global->linux.hwCnts.softCntEdge,
    475             run->global->linux.hwCnts.softCntPc, run->global->linux.hwCnts.softCntCmp);
    476 
    477         fuzz_addFileToFileQ(run);
    478     }
    479 }
    480 
    481 static void fuzz_sanCovFeedback(run_t* run) {
    482     if (run->global->skipFeedbackOnTimeout && run->tmOutSignaled) {
    483         return;
    484     }
    485 
    486     LOG_D("File size (Best/New): %zu, SanCov feedback (bb,dso): Best: [%" PRIu64 ",%" PRIu64
    487           "] / New: [%" PRIu64 ",%" PRIu64 "], newBBs:%" PRIu64,
    488         run->dynamicFileSz, run->global->sanCovCnts.hitBBCnt, run->global->sanCovCnts.iDsoCnt,
    489         run->sanCovCnts.hitBBCnt, run->sanCovCnts.iDsoCnt, run->sanCovCnts.newBBCnt);
    490 
    491     MX_SCOPED_LOCK(&run->global->feedback_mutex);
    492 
    493     int64_t diff0 = run->global->linux.hwCnts.cpuInstrCnt - run->linux.hwCnts.cpuInstrCnt;
    494     int64_t diff1 = run->global->linux.hwCnts.cpuBranchCnt - run->linux.hwCnts.cpuBranchCnt;
    495 
    496     /*
    497      * Keep mutated seed if:
    498      *  a) Newly discovered (not met before) BBs
    499      *  b) More instrumented DSOs loaded
    500      *
    501      * TODO: (a) method can significantly assist to further improvements in interesting areas
    502      * discovery if combined with seeds pool/queue support. If a runtime queue is maintained
    503      * more interesting seeds can be saved between runs instead of instantly discarded
    504      * based on current absolute elitism (only one mutated seed is promoted).
    505      */
    506 
    507     bool newCov =
    508         (run->sanCovCnts.newBBCnt > 0 || run->global->sanCovCnts.iDsoCnt < run->sanCovCnts.iDsoCnt);
    509 
    510     if (newCov || (diff0 < 0 || diff1 < 0)) {
    511         LOG_I("SanCov Update: fsize:%zu, newBBs:%" PRIu64 ", (Cur,New): %" PRIu64 "/%" PRIu64
    512               ",%" PRIu64 "/%" PRIu64,
    513             run->dynamicFileSz, run->sanCovCnts.newBBCnt, run->global->sanCovCnts.hitBBCnt,
    514             run->global->sanCovCnts.iDsoCnt, run->sanCovCnts.hitBBCnt, run->sanCovCnts.iDsoCnt);
    515 
    516         run->global->sanCovCnts.hitBBCnt += run->sanCovCnts.newBBCnt;
    517         run->global->sanCovCnts.dsoCnt = run->sanCovCnts.dsoCnt;
    518         run->global->sanCovCnts.iDsoCnt = run->sanCovCnts.iDsoCnt;
    519         run->global->sanCovCnts.crashesCnt += run->sanCovCnts.crashesCnt;
    520         run->global->sanCovCnts.newBBCnt = run->sanCovCnts.newBBCnt;
    521 
    522         if (run->global->sanCovCnts.totalBBCnt < run->sanCovCnts.totalBBCnt) {
    523             /* Keep only the max value (for dlopen cases) to measure total target coverage */
    524             run->global->sanCovCnts.totalBBCnt = run->sanCovCnts.totalBBCnt;
    525         }
    526 
    527         run->global->linux.hwCnts.cpuInstrCnt = run->linux.hwCnts.cpuInstrCnt;
    528         run->global->linux.hwCnts.cpuBranchCnt = run->linux.hwCnts.cpuBranchCnt;
    529 
    530         fuzz_addFileToFileQ(run);
    531     }
    532 }
    533 
    534 static void fuzz_fuzzLoop(run_t* run) {
    535     run->pid = 0;
    536     run->timeStartedMillis = util_timeNowMillis();
    537     run->state = fuzz_getState(run->global);
    538     run->crashFileName[0] = '\0';
    539     run->pc = 0ULL;
    540     run->backtrace = 0ULL;
    541     run->access = 0ULL;
    542     run->exception = 0;
    543     run->report[0] = '\0';
    544     run->mainWorker = true;
    545     run->origFileName = "DYNAMIC";
    546     run->mutationsPerRun = run->global->mutationsPerRun;
    547     run->dynamicFileSz = 0;
    548 
    549     run->sanCovCnts.hitBBCnt = 0ULL;
    550     run->sanCovCnts.totalBBCnt = 0ULL;
    551     run->sanCovCnts.dsoCnt = 0ULL;
    552     run->sanCovCnts.newBBCnt = 0ULL;
    553     run->sanCovCnts.crashesCnt = 0ULL;
    554 
    555     run->linux.hwCnts.cpuInstrCnt = 0ULL;
    556     run->linux.hwCnts.cpuBranchCnt = 0ULL;
    557     run->linux.hwCnts.bbCnt = 0ULL;
    558     run->linux.hwCnts.newBBCnt = 0ULL;
    559 
    560     if (run->state == _HF_STATE_DYNAMIC_PRE) {
    561         run->mutationsPerRun = 0U;
    562         if (fuzz_prepareFile(run, false /* rewind */) == false) {
    563             fuzz_setState(run->global, _HF_STATE_DYNAMIC_MAIN);
    564             run->state = fuzz_getState(run->global);
    565         }
    566     }
    567 
    568     if (fuzz_isTerminating()) {
    569         return;
    570     }
    571 
    572     if (run->state == _HF_STATE_DYNAMIC_MAIN) {
    573         if (run->global->exe.externalCommand) {
    574             if (!fuzz_prepareFileExternally(run)) {
    575                 LOG_F("fuzz_prepareFileExternally() failed");
    576             }
    577         } else if (!fuzz_prepareFileDynamically(run)) {
    578             LOG_F("fuzz_prepareFileDynamically() failed");
    579         }
    580 
    581         if (run->global->exe.postExternalCommand) {
    582             if (!fuzz_postProcessFile(run)) {
    583                 LOG_F("fuzz_postProcessFile() failed");
    584             }
    585         }
    586     }
    587 
    588     if (run->state == _HF_STATE_STATIC) {
    589         if (run->global->exe.externalCommand) {
    590             if (!fuzz_prepareFileExternally(run)) {
    591                 LOG_F("fuzz_prepareFileExternally() failed");
    592             }
    593         } else {
    594             if (!fuzz_prepareFile(run, true /* rewind */)) {
    595                 LOG_F("fuzz_prepareFile() failed");
    596             }
    597         }
    598 
    599         if (run->global->exe.postExternalCommand != NULL) {
    600             if (!fuzz_postProcessFile(run)) {
    601                 LOG_F("fuzz_postProcessFile() failed");
    602             }
    603         }
    604     }
    605 
    606     if (subproc_Run(run) == false) {
    607         LOG_F("subproc_Run()");
    608     }
    609 
    610     if (run->global->persistent == false) {
    611         unlink(run->fileName);
    612     }
    613 
    614     if (run->global->dynFileMethod != _HF_DYNFILE_NONE) {
    615         fuzz_perfFeedback(run);
    616     }
    617     if (run->global->useSanCov) {
    618         fuzz_sanCovFeedback(run);
    619     }
    620 
    621     if (run->global->useVerifier && (run->crashFileName[0] != 0) && run->backtrace) {
    622         if (!fuzz_runVerifier(run)) {
    623             LOG_I("Failed to verify %s", run->crashFileName);
    624         }
    625     }
    626 
    627     report_Report(run);
    628 }
    629 
    630 static void* fuzz_threadNew(void* arg) {
    631     honggfuzz_t* hfuzz = (honggfuzz_t*)arg;
    632     unsigned int fuzzNo = ATOMIC_POST_INC(hfuzz->threads.threadsActiveCnt);
    633     LOG_I("Launched new fuzzing thread, no. #%" PRId32, fuzzNo);
    634 
    635     run_t run = {
    636         .global = hfuzz,
    637         .pid = 0,
    638         .persistentPid = 0,
    639         .dynfileqCurrent = NULL,
    640         .dynamicFile = util_Calloc(hfuzz->maxFileSz),
    641         .fuzzNo = fuzzNo,
    642         .persistentSock = -1,
    643         .tmOutSignaled = false,
    644         .fileName = "[UNSET]",
    645 
    646         .linux.attachedPid = 0,
    647     };
    648     defer { free(run.dynamicFile); };
    649     fuzz_getFileName(&run);
    650 
    651     if (arch_archThreadInit(&run) == false) {
    652         LOG_F("Could not initialize the thread");
    653     }
    654 
    655     for (;;) {
    656         /* Check if dry run mode with verifier enabled */
    657         if (run.global->mutationsPerRun == 0U && run.global->useVerifier) {
    658             if (ATOMIC_POST_INC(run.global->cnts.mutationsCnt) >= run.global->io.fileCnt) {
    659                 ATOMIC_POST_INC(run.global->threads.threadsFinished);
    660                 break;
    661             }
    662         }
    663         /* Check for max iterations limit if set */
    664         else if ((ATOMIC_POST_INC(run.global->cnts.mutationsCnt) >= run.global->mutationsMax) &&
    665                  run.global->mutationsMax) {
    666             ATOMIC_POST_INC(run.global->threads.threadsFinished);
    667             break;
    668         }
    669 
    670         fuzz_fuzzLoop(&run);
    671 
    672         if (fuzz_isTerminating()) {
    673             break;
    674         }
    675 
    676         if (run.global->exitUponCrash && ATOMIC_GET(run.global->cnts.crashesCnt) > 0) {
    677             LOG_I("Seen a crash. Terminating all fuzzing threads");
    678             fuzz_setTerminating();
    679             break;
    680         }
    681     }
    682 
    683     LOG_I("Terminating thread no. #%" PRId32, fuzzNo);
    684     ATOMIC_POST_INC(run.global->threads.threadsFinished);
    685     pthread_kill(run.global->threads.mainThread, SIGALRM);
    686     return NULL;
    687 }
    688 
    689 static void fuzz_runThread(honggfuzz_t* hfuzz, pthread_t* thread, void* (*thread_func)(void*)) {
    690     pthread_attr_t attr;
    691 
    692     pthread_attr_init(&attr);
    693     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    694     pthread_attr_setstacksize(&attr, _HF_PTHREAD_STACKSIZE);
    695     pthread_attr_setguardsize(&attr, (size_t)sysconf(_SC_PAGESIZE));
    696 
    697     if (pthread_create(thread, &attr, thread_func, (void*)hfuzz) < 0) {
    698         PLOG_F("Couldn't create a new thread");
    699     }
    700 
    701     pthread_attr_destroy(&attr);
    702 
    703     return;
    704 }
    705 
    706 void fuzz_threadsStart(honggfuzz_t* hfuzz, pthread_t* threads) {
    707     if (!arch_archInit(hfuzz)) {
    708         LOG_F("Couldn't prepare arch for fuzzing");
    709     }
    710     if (!sanitizers_Init(hfuzz)) {
    711         LOG_F("Couldn't prepare sanitizer options");
    712     }
    713     if (!sancov_Init(hfuzz)) {
    714         LOG_F("Couldn't prepare sancov options");
    715     }
    716 
    717     if (hfuzz->useSanCov || hfuzz->dynFileMethod != _HF_DYNFILE_NONE) {
    718         fuzz_setState(hfuzz, _HF_STATE_DYNAMIC_PRE);
    719     } else {
    720         fuzz_setState(hfuzz, _HF_STATE_STATIC);
    721     }
    722 
    723     for (size_t i = 0; i < hfuzz->threads.threadsMax; i++) {
    724         fuzz_runThread(hfuzz, &threads[i], fuzz_threadNew);
    725     }
    726 }
    727 
    728 void fuzz_threadsStop(honggfuzz_t* hfuzz, pthread_t* threads) {
    729     for (size_t i = 0; i < hfuzz->threads.threadsMax; i++) {
    730         void* retval;
    731         if (pthread_join(threads[i], &retval) != 0) {
    732             PLOG_F("Couldn't pthread_join() thread: %zu", i);
    733         }
    734     }
    735     LOG_I("All threads done");
    736 }
    737