1/*
2 *
3 * honggfuzz - fuzzing routines
4 * -----------------------------------------
5 *
6 * Author:
7 * Robert Swiecki <swiecki@google.com>
8 * Felix Gröbert <groebert@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
60static time_t termTimeStamp = 0;
61
62bool fuzz_isTerminating(void) {
63    if (ATOMIC_GET(termTimeStamp) != 0) {
64        return true;
65    }
66    return false;
67}
68
69void fuzz_setTerminating(void) {
70    if (ATOMIC_GET(termTimeStamp) != 0) {
71        return;
72    }
73    ATOMIC_SET(termTimeStamp, time(NULL));
74}
75
76bool 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
86static 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
93static 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
132static 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
158static 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
199static 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
225static fuzzState_t fuzz_getState(honggfuzz_t* hfuzz) { return ATOMIC_GET(hfuzz->state); }
226
227static 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
265static 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
375static 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
399static 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
423static 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
481static 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
534static 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
630static 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
689static 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
706void 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
728void 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