"Fossies" - the Fresh Open Source Software Archive

Member "honggfuzz-2.2/fuzz.c" (23 Apr 2020, 20296 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 "fuzz.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  *
    3  * honggfuzz - fuzzing routines
    4  * -----------------------------------------
    5  *
    6  * Authors: Robert Swiecki <swiecki@google.com>
    7  *          Felix Gröbert <groebert@google.com>
    8  *
    9  * Copyright 2010-2018 by Google Inc. All Rights Reserved.
   10  *
   11  * Licensed under the Apache License, Version 2.0 (the "License"); you may
   12  * not use this file except in compliance with the License. You may obtain
   13  * a copy of the License at
   14  *
   15  * http://www.apache.org/licenses/LICENSE-2.0
   16  *
   17  * Unless required by applicable law or agreed to in writing, software
   18  * distributed under the License is distributed on an "AS IS" BASIS,
   19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   20  * implied. See the License for the specific language governing
   21  * permissions and limitations under the License.
   22  *
   23  */
   24 
   25 #include "fuzz.h"
   26 
   27 #include <errno.h>
   28 #include <fcntl.h>
   29 #include <inttypes.h>
   30 #include <libgen.h>
   31 #include <math.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 "libhfcommon/common.h"
   51 #include "libhfcommon/files.h"
   52 #include "libhfcommon/log.h"
   53 #include "libhfcommon/util.h"
   54 #include "mangle.h"
   55 #include "report.h"
   56 #include "sanitizers.h"
   57 #include "socketfuzzer.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 fuzzState_t fuzz_getState(honggfuzz_t* hfuzz) {
   87     return ATOMIC_GET(hfuzz->feedback.state);
   88 }
   89 
   90 static void fuzz_setDynamicMainState(run_t* run) {
   91     /* All threads need to indicate willingness to switch to the DYNAMIC_MAIN state. Count them! */
   92     static uint32_t cnt = 0;
   93     ATOMIC_PRE_INC(cnt);
   94 
   95     static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
   96     MX_SCOPED_LOCK(&state_mutex);
   97 
   98     if (fuzz_getState(run->global) != _HF_STATE_DYNAMIC_DRY_RUN) {
   99         /* Already switched out of the Dry Run */
  100         return;
  101     }
  102 
  103     LOG_I("Entering phase 2/3: Switching to the Feedback Driven Mode");
  104     ATOMIC_SET(run->global->cfg.switchingToFDM, true);
  105 
  106     for (;;) {
  107         /* Check if all threads have already reported in for changing state */
  108         if (ATOMIC_GET(cnt) == run->global->threads.threadsMax) {
  109             break;
  110         }
  111         if (fuzz_isTerminating()) {
  112             return;
  113         }
  114         util_sleepForMSec(10); /* Check every 10ms */
  115     }
  116 
  117     ATOMIC_SET(run->global->cfg.switchingToFDM, false);
  118 
  119     if (run->global->cfg.minimize) {
  120         LOG_I("Entering phase 3/3: Corpus Minimization");
  121         ATOMIC_SET(run->global->feedback.state, _HF_STATE_DYNAMIC_MINIMIZE);
  122         return;
  123     }
  124 
  125     /*
  126      * If the initial fuzzing yielded no useful coverage, just add a single empty file to the
  127      * dynamic corpus, so the dynamic phase doesn't fail because of lack of useful inputs
  128      */
  129     if (run->global->io.dynfileqCnt == 0) {
  130         dynfile_t dynfile = {
  131             .size = 0,
  132             .cov = {},
  133             .idx = 0,
  134             .fd = -1,
  135             .timeExecUSecs = 1,
  136             .path = "[DYNAMIC-0-SIZE]",
  137             .data = (uint8_t*)"",
  138         };
  139         dynfile_t* tmp_dynfile = run->dynfile;
  140         run->dynfile = &dynfile;
  141         input_addDynamicInput(run);
  142         run->dynfile = tmp_dynfile;
  143     }
  144     snprintf(run->dynfile->path, sizeof(run->dynfile->path), "[DYNAMIC]");
  145 
  146     if (run->global->io.maxFileSz == 0 && run->global->mutate.maxInputSz > _HF_INPUT_DEFAULT_SIZE) {
  147         size_t newsz = (run->global->io.dynfileqMaxSz >= _HF_INPUT_DEFAULT_SIZE)
  148                            ? run->global->io.dynfileqMaxSz
  149                            : _HF_INPUT_DEFAULT_SIZE;
  150         newsz = (newsz + newsz / 4); /* Add 25% overhead for growth */
  151         if (newsz > run->global->mutate.maxInputSz) {
  152             newsz = run->global->mutate.maxInputSz;
  153         }
  154         LOG_I("Setting maximum input size to %zu bytes (previously %zu bytes)", newsz,
  155             run->global->mutate.maxInputSz);
  156         run->global->mutate.maxInputSz = newsz;
  157     }
  158 
  159     LOG_I("Entering phase 3/3: Dynamic Main (Feedback Driven Mode)");
  160     ATOMIC_SET(run->global->feedback.state, _HF_STATE_DYNAMIC_MAIN);
  161 }
  162 
  163 static void fuzz_minimizeRemoveFiles(run_t* run) {
  164     if (run->global->io.outputDir) {
  165         LOG_I("Minimized files were copied to '%s'", run->global->io.outputDir);
  166         return;
  167     }
  168     if (!input_getDirStatsAndRewind(run->global)) {
  169         return;
  170     }
  171     for (;;) {
  172         char fname[PATH_MAX];
  173         if (!input_getNext(run, fname, /* rewind= */ false)) {
  174             break;
  175         }
  176         if (!input_inDynamicCorpus(run, fname)) {
  177             if (input_removeStaticFile(run->global->io.inputDir, fname)) {
  178                 LOG_I("Removed unnecessary '%s'", fname);
  179             }
  180         }
  181     }
  182     LOG_I("Corpus minimization done");
  183 }
  184 
  185 static void fuzz_perfFeedback(run_t* run) {
  186     if (run->global->feedback.skipFeedbackOnTimeout && run->tmOutSignaled) {
  187         return;
  188     }
  189 
  190     MX_SCOPED_LOCK(&run->global->feedback.covFeedback_mutex);
  191     defer {
  192         wmb();
  193     };
  194 
  195     uint64_t softNewPC = ATOMIC_GET(run->global->feedback.covFeedbackMap->pidNewPC[run->fuzzNo]);
  196     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidNewPC[run->fuzzNo]);
  197     uint64_t softCurPC = ATOMIC_GET(run->global->feedback.covFeedbackMap->pidTotalPC[run->fuzzNo]);
  198     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidTotalPC[run->fuzzNo]);
  199 
  200     uint64_t softNewEdge =
  201         ATOMIC_GET(run->global->feedback.covFeedbackMap->pidNewEdge[run->fuzzNo]);
  202     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidNewEdge[run->fuzzNo]);
  203     uint64_t softCurEdge =
  204         ATOMIC_GET(run->global->feedback.covFeedbackMap->pidTotalEdge[run->fuzzNo]);
  205     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidTotalEdge[run->fuzzNo]);
  206 
  207     uint64_t softNewCmp = ATOMIC_GET(run->global->feedback.covFeedbackMap->pidNewCmp[run->fuzzNo]);
  208     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidNewCmp[run->fuzzNo]);
  209     uint64_t softCurCmp =
  210         ATOMIC_GET(run->global->feedback.covFeedbackMap->pidTotalCmp[run->fuzzNo]);
  211     ATOMIC_CLEAR(run->global->feedback.covFeedbackMap->pidTotalCmp[run->fuzzNo]);
  212 
  213     rmb();
  214 
  215     int64_t diff0 = (int64_t)run->global->feedback.hwCnts.cpuInstrCnt - run->hwCnts.cpuInstrCnt;
  216     int64_t diff1 = (int64_t)run->global->feedback.hwCnts.cpuBranchCnt - run->hwCnts.cpuBranchCnt;
  217 
  218     /* Any increase in coverage (edge, pc, cmp, hw) counters forces adding input to the corpus */
  219     if (run->hwCnts.newBBCnt > 0 || softNewPC > 0 || softNewEdge > 0 || softNewCmp > 0 ||
  220         diff0 < 0 || diff1 < 0) {
  221         if (diff0 < 0) {
  222             run->global->feedback.hwCnts.cpuInstrCnt = run->hwCnts.cpuInstrCnt;
  223         }
  224         if (diff1 < 0) {
  225             run->global->feedback.hwCnts.cpuBranchCnt = run->hwCnts.cpuBranchCnt;
  226         }
  227         run->global->feedback.hwCnts.bbCnt += run->hwCnts.newBBCnt;
  228         run->global->feedback.hwCnts.softCntPc += softNewPC;
  229         run->global->feedback.hwCnts.softCntEdge += softNewEdge;
  230         run->global->feedback.hwCnts.softCntCmp += softNewCmp;
  231 
  232         LOG_I("Sz:%zu Tm:%" _HF_NONMON_SEP PRIu64 "us (i/b/h/e/p/c) New:%" PRIu64 "/%" PRIu64
  233               "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 ", Cur:%" PRIu64 "/%" PRIu64
  234               "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64,
  235             run->dynfile->size, util_timeNowUSecs() - run->timeStartedUSecs,
  236             run->hwCnts.cpuInstrCnt, run->hwCnts.cpuBranchCnt, run->hwCnts.newBBCnt, softNewEdge,
  237             softNewPC, softNewCmp, run->hwCnts.cpuInstrCnt, run->hwCnts.cpuBranchCnt,
  238             run->hwCnts.bbCnt, softCurEdge, softCurPC, softCurCmp);
  239 
  240         /* Update per-input coverage metrics */
  241         run->dynfile->cov[0] = softCurEdge + softCurPC + run->hwCnts.bbCnt;
  242         run->dynfile->cov[1] = softCurCmp;
  243         run->dynfile->cov[2] = run->hwCnts.cpuInstrCnt + run->hwCnts.cpuBranchCnt;
  244         run->dynfile->cov[3] = run->dynfile->size ? (64 - util_Log2(run->dynfile->size)) : 64;
  245         input_addDynamicInput(run);
  246 
  247         if (run->global->socketFuzzer.enabled) {
  248             LOG_D("SocketFuzzer: fuzz: new BB (perf)");
  249             fuzz_notifySocketFuzzerNewCov(run->global);
  250         }
  251     }
  252 }
  253 
  254 /* Return value indicates whether report file should be updated with the current verified crash */
  255 static bool fuzz_runVerifier(run_t* run) {
  256     if (!run->crashFileName[0] || !run->backtrace) {
  257         return false;
  258     }
  259 
  260     uint64_t backtrace = run->backtrace;
  261 
  262     char origCrashPath[PATH_MAX];
  263     snprintf(origCrashPath, sizeof(origCrashPath), "%s", run->crashFileName);
  264     /* Workspace is inherited, just append a extra suffix */
  265     char verFile[PATH_MAX];
  266     snprintf(verFile, sizeof(verFile), "%s.verified", origCrashPath);
  267 
  268     if (files_exists(verFile)) {
  269         LOG_D("Crash file to verify '%s' is already verified as '%s'", origCrashPath, verFile);
  270         return false;
  271     }
  272 
  273     for (int i = 0; i < _HF_VERIFIER_ITER; i++) {
  274         LOG_I("Launching verifier for HASH: %" PRIx64 " (iteration: %d out of %d)", run->backtrace,
  275             i + 1, _HF_VERIFIER_ITER);
  276         run->timeStartedUSecs = util_timeNowUSecs();
  277         run->backtrace = 0;
  278         run->access = 0;
  279         run->exception = 0;
  280         run->mainWorker = false;
  281 
  282         if (!subproc_Run(run)) {
  283             LOG_F("subproc_Run()");
  284         }
  285 
  286         /* If stack hash doesn't match skip name tag and exit */
  287         if (run->backtrace != backtrace) {
  288             LOG_E("Verifier stack mismatch: (original) %" PRIx64 " != (new) %" PRIx64, backtrace,
  289                 run->backtrace);
  290             run->backtrace = backtrace;
  291             return true;
  292         }
  293 
  294         LOG_I("Verifier for HASH: %" PRIx64 " (iteration: %d, left: %d). MATCH!", run->backtrace,
  295             i + 1, _HF_VERIFIER_ITER - i - 1);
  296     }
  297 
  298     /* Copy file with new suffix & remove original copy */
  299     int fd = TEMP_FAILURE_RETRY(open(verFile, O_CREAT | O_EXCL | O_WRONLY, 0600));
  300     if (fd == -1 && errno == EEXIST) {
  301         LOG_I("It seems that '%s' already exists, skipping", verFile);
  302         return false;
  303     }
  304     if (fd == -1) {
  305         PLOG_E("Couldn't create '%s'", verFile);
  306         return true;
  307     }
  308     defer {
  309         close(fd);
  310     };
  311     if (!files_writeToFd(fd, run->dynfile->data, run->dynfile->size)) {
  312         LOG_E("Couldn't save verified file as '%s'", verFile);
  313         unlink(verFile);
  314         return true;
  315     }
  316 
  317     LOG_I("Verified crash for HASH: %" PRIx64 " and saved it as '%s'", backtrace, verFile);
  318     ATOMIC_PRE_INC(run->global->cnts.verifiedCrashesCnt);
  319 
  320     return true;
  321 }
  322 
  323 static bool fuzz_fetchInput(run_t* run) {
  324     {
  325         fuzzState_t st = fuzz_getState(run->global);
  326         if (st == _HF_STATE_DYNAMIC_DRY_RUN) {
  327             run->mutationsPerRun = 0U;
  328             if (input_prepareStaticFile(run, /* rewind= */ false, true)) {
  329                 return true;
  330             }
  331             fuzz_setDynamicMainState(run);
  332             run->mutationsPerRun = run->global->mutate.mutationsPerRun;
  333         }
  334     }
  335 
  336     if (fuzz_getState(run->global) == _HF_STATE_DYNAMIC_MINIMIZE) {
  337         fuzz_minimizeRemoveFiles(run);
  338         return false;
  339     }
  340 
  341     if (fuzz_getState(run->global) == _HF_STATE_DYNAMIC_MAIN) {
  342         if (run->global->exe.externalCommand) {
  343             if (!input_prepareExternalFile(run)) {
  344                 LOG_E("input_prepareExternalFile() failed");
  345                 return false;
  346             }
  347         } else if (run->global->exe.feedbackMutateCommand) {
  348             if (!input_prepareDynamicInput(run, false)) {
  349                 LOG_E("input_prepareDynamicInput(() failed");
  350                 return false;
  351             }
  352         } else if (!input_prepareDynamicInput(run, true)) {
  353             LOG_E("input_prepareDynamicInput() failed");
  354             return false;
  355         }
  356     }
  357 
  358     if (fuzz_getState(run->global) == _HF_STATE_STATIC) {
  359         if (run->global->exe.externalCommand) {
  360             if (!input_prepareExternalFile(run)) {
  361                 LOG_E("input_prepareExternalFile() failed");
  362                 return false;
  363             }
  364         } else if (run->global->exe.feedbackMutateCommand) {
  365             if (!input_prepareStaticFile(run, true, false)) {
  366                 LOG_E("input_prepareStaticFile() failed");
  367                 return false;
  368             }
  369         } else if (!input_prepareStaticFile(run, true /* rewind */, true)) {
  370             LOG_E("input_prepareStaticFile() failed");
  371             return false;
  372         }
  373     }
  374 
  375     if (run->global->exe.postExternalCommand &&
  376         !input_postProcessFile(run, run->global->exe.postExternalCommand)) {
  377         LOG_E("input_postProcessFile('%s') failed", run->global->exe.postExternalCommand);
  378         return false;
  379     }
  380 
  381     if (run->global->exe.feedbackMutateCommand &&
  382         !input_postProcessFile(run, run->global->exe.feedbackMutateCommand)) {
  383         LOG_E("input_postProcessFile('%s') failed", run->global->exe.feedbackMutateCommand);
  384         return false;
  385     }
  386 
  387     return true;
  388 }
  389 
  390 static void fuzz_fuzzLoop(run_t* run) {
  391     run->timeStartedUSecs = util_timeNowUSecs();
  392     run->crashFileName[0] = '\0';
  393     run->pc = 0;
  394     run->backtrace = 0;
  395     run->access = 0;
  396     run->exception = 0;
  397     run->report[0] = '\0';
  398     run->mainWorker = true;
  399     run->mutationsPerRun = run->global->mutate.mutationsPerRun;
  400     run->tmOutSignaled = false;
  401 
  402     run->hwCnts.cpuInstrCnt = 0;
  403     run->hwCnts.cpuBranchCnt = 0;
  404     run->hwCnts.bbCnt = 0;
  405     run->hwCnts.newBBCnt = 0;
  406 
  407     if (!fuzz_fetchInput(run)) {
  408         if (run->global->cfg.minimize && fuzz_getState(run->global) == _HF_STATE_DYNAMIC_MINIMIZE) {
  409             fuzz_setTerminating();
  410             return;
  411         }
  412         LOG_F("Cound't prepare input for fuzzing");
  413     }
  414     if (!subproc_Run(run)) {
  415         LOG_F("Couldn't run fuzzed command");
  416     }
  417 
  418     if (run->global->feedback.dynFileMethod != _HF_DYNFILE_NONE) {
  419         fuzz_perfFeedback(run);
  420     }
  421     if (run->global->cfg.useVerifier && !fuzz_runVerifier(run)) {
  422         return;
  423     }
  424     report_saveReport(run);
  425 }
  426 
  427 static void fuzz_fuzzLoopSocket(run_t* run) {
  428     run->timeStartedUSecs = util_timeNowUSecs();
  429     run->crashFileName[0] = '\0';
  430     run->pc = 0;
  431     run->backtrace = 0;
  432     run->access = 0;
  433     run->exception = 0;
  434     run->report[0] = '\0';
  435     run->mainWorker = true;
  436     run->mutationsPerRun = run->global->mutate.mutationsPerRun;
  437     run->tmOutSignaled = false;
  438 
  439     run->hwCnts.cpuInstrCnt = 0;
  440     run->hwCnts.cpuBranchCnt = 0;
  441     run->hwCnts.bbCnt = 0;
  442     run->hwCnts.newBBCnt = 0;
  443 
  444     LOG_I("------------------------------------------------------");
  445 
  446     /* First iteration: Start target
  447        Other iterations: re-start target, if necessary
  448        subproc_Run() will decide by itself if a restart is necessary, via
  449        subproc_New()
  450     */
  451     LOG_D("------[ 1: subproc_run");
  452     if (!subproc_Run(run)) {
  453         LOG_W("Couldn't run server");
  454     }
  455 
  456     /* Tell the external fuzzer to send data to target
  457        The fuzzer will notify us when finished; block until then.
  458     */
  459     LOG_D("------[ 2: fetch input");
  460     if (!fuzz_waitForExternalInput(run)) {
  461         /* Fuzzer could not connect to target, and told us to
  462            restart it. Do it on the next iteration.
  463            or: it crashed by fuzzing. Restart it too.
  464            */
  465         LOG_D("------[ 2.1: Target down, will restart it");
  466         run->pid = 0;  // make subproc_Run() restart it on next iteration
  467         return;
  468     }
  469 
  470     LOG_D("------[ 3: feedback");
  471     if (run->global->feedback.dynFileMethod != _HF_DYNFILE_NONE) {
  472         fuzz_perfFeedback(run);
  473     }
  474     if (run->global->cfg.useVerifier && !fuzz_runVerifier(run)) {
  475         return;
  476     }
  477 
  478     report_saveReport(run);
  479 }
  480 
  481 static void* fuzz_threadNew(void* arg) {
  482     honggfuzz_t* hfuzz = (honggfuzz_t*)arg;
  483     unsigned int fuzzNo = ATOMIC_POST_INC(hfuzz->threads.threadsActiveCnt);
  484     LOG_I("Launched new fuzzing thread, no. #%u", fuzzNo);
  485 
  486     run_t run = {
  487         .global = hfuzz,
  488         .pid = 0,
  489         .dynfile = (dynfile_t*)util_Malloc(sizeof(dynfile_t) + hfuzz->io.maxFileSz),
  490         .fuzzNo = fuzzNo,
  491         .persistentSock = -1,
  492         .tmOutSignaled = false,
  493     };
  494 
  495     /* Do not try to handle input files with socketfuzzer */
  496     char mapname[32];
  497     snprintf(mapname, sizeof(mapname), "hf-%u-input", fuzzNo);
  498     if (!hfuzz->socketFuzzer.enabled) {
  499         if (!(run.dynfile->data = files_mapSharedMem(hfuzz->mutate.maxInputSz, &(run.dynfile->fd),
  500                   mapname, /* nocore= */ true, /* exportmap= */ false))) {
  501             LOG_F("Couldn't create an input file of size: %zu, name:'%s'", hfuzz->mutate.maxInputSz,
  502                 mapname);
  503         }
  504     }
  505     defer {
  506         if (run.dynfile->fd != -1) {
  507             close(run.dynfile->fd);
  508         }
  509     };
  510 
  511     snprintf(mapname, sizeof(mapname), "hf-%u-perthreadmap", fuzzNo);
  512     if ((run.perThreadCovFeedbackFd = files_createSharedMem(sizeof(feedback_t), mapname,
  513              /* exportmap= */ run.global->io.exportFeedback)) == -1) {
  514         LOG_F("files_createSharedMem(name='%s', sz=%zu, dir='%s') failed", mapname,
  515             sizeof(feedback_t), run.global->io.workDir);
  516     }
  517     defer {
  518         if (run.perThreadCovFeedbackFd != -1) {
  519             close(run.perThreadCovFeedbackFd);
  520         }
  521     };
  522 
  523     if (!arch_archThreadInit(&run)) {
  524         LOG_F("Could not initialize the thread");
  525     }
  526 
  527     for (;;) {
  528         /* Check if dry run mode with verifier enabled */
  529         if (run.global->mutate.mutationsPerRun == 0U && run.global->cfg.useVerifier &&
  530             !hfuzz->socketFuzzer.enabled) {
  531             if (ATOMIC_POST_INC(run.global->cnts.mutationsCnt) >= run.global->io.fileCnt) {
  532                 break;
  533             }
  534         }
  535         /* Check for max iterations limit if set */
  536         else if ((ATOMIC_POST_INC(run.global->cnts.mutationsCnt) >=
  537                      run.global->mutate.mutationsMax) &&
  538                  run.global->mutate.mutationsMax) {
  539             break;
  540         }
  541 
  542         if (hfuzz->socketFuzzer.enabled) {
  543             fuzz_fuzzLoopSocket(&run);
  544         } else {
  545             fuzz_fuzzLoop(&run);
  546         }
  547 
  548         if (fuzz_isTerminating()) {
  549             break;
  550         }
  551 
  552         if (run.global->cfg.exitUponCrash && ATOMIC_GET(run.global->cnts.crashesCnt) > 0) {
  553             LOG_I("Seen a crash. Terminating all fuzzing threads");
  554             fuzz_setTerminating();
  555             break;
  556         }
  557     }
  558 
  559     if (run.pid) {
  560         kill(run.pid, SIGKILL);
  561     }
  562 
  563     size_t j = ATOMIC_PRE_INC(run.global->threads.threadsFinished);
  564     LOG_I("Terminating thread no. #%" PRId32 ", left: %zu", fuzzNo, hfuzz->threads.threadsMax - j);
  565     return NULL;
  566 }
  567 
  568 void fuzz_threadsStart(honggfuzz_t* hfuzz) {
  569     if (!arch_archInit(hfuzz)) {
  570         LOG_F("Couldn't prepare arch for fuzzing");
  571     }
  572     if (!sanitizers_Init(hfuzz)) {
  573         LOG_F("Couldn't prepare sanitizer options");
  574     }
  575 
  576     if (hfuzz->socketFuzzer.enabled) {
  577         /* Don't do dry run with socketFuzzer */
  578         LOG_I("Entering phase - Feedback Driven Mode (SocketFuzzer)");
  579         hfuzz->feedback.state = _HF_STATE_DYNAMIC_MAIN;
  580     } else if (hfuzz->feedback.dynFileMethod != _HF_DYNFILE_NONE) {
  581         LOG_I("Entering phase 1/3: Dry Run");
  582         hfuzz->feedback.state = _HF_STATE_DYNAMIC_DRY_RUN;
  583     } else {
  584         LOG_I("Entering phase: Static");
  585         hfuzz->feedback.state = _HF_STATE_STATIC;
  586     }
  587 
  588     for (size_t i = 0; i < hfuzz->threads.threadsMax; i++) {
  589         if (!subproc_runThread(
  590                 hfuzz, &hfuzz->threads.threads[i], fuzz_threadNew, /* joinable= */ true)) {
  591             PLOG_F("Couldn't run a thread #%zu", i);
  592         }
  593     }
  594 }