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 "common.h"
27 #include "fuzz.h"
28 
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <inttypes.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/param.h>
40 #include <sys/resource.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <time.h>
47 #include <unistd.h>
48 
49 #include "arch.h"
50 #include "files.h"
51 #include "log.h"
52 #include "mangle.h"
53 #include "report.h"
54 #include "sancov.h"
55 #include "util.h"
56 
57 extern char **environ;
58 
59 static pthread_t fuzz_mainThread;
60 
fuzz_getFileName(honggfuzz_t * hfuzz,char * fileName)61 static void fuzz_getFileName(honggfuzz_t * hfuzz, char *fileName)
62 {
63     struct timeval tv;
64     gettimeofday(&tv, NULL);
65 
66     snprintf(fileName, PATH_MAX, "%s/.honggfuzz.%d.%lu.%llx.%s", hfuzz->workDir, (int)getpid(),
67              (unsigned long int)tv.tv_sec, (unsigned long long int)util_rndGet(0, 1ULL << 62),
68              hfuzz->fileExtn);
69 }
70 
fuzz_prepareExecve(honggfuzz_t * hfuzz,const char * fileName)71 static bool fuzz_prepareExecve(honggfuzz_t * hfuzz, const char *fileName)
72 {
73     /*
74      * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof)
75      */
76     if (hfuzz->tmOut) {
77         struct itimerval it;
78 
79         /*
80          * The hfuzz->tmOut is real CPU usage time...
81          */
82         it.it_value.tv_sec = hfuzz->tmOut;
83         it.it_value.tv_usec = 0;
84         it.it_interval.tv_sec = 0;
85         it.it_interval.tv_usec = 0;
86         if (setitimer(ITIMER_PROF, &it, NULL) == -1) {
87             PLOG_D("Couldn't set the ITIMER_PROF timer");
88         }
89 
90         /*
91          * ...so, if a process sleeps, this one should
92          * trigger a signal...
93          */
94         it.it_value.tv_sec = hfuzz->tmOut;
95         it.it_value.tv_usec = 0;
96         it.it_interval.tv_sec = 0;
97         it.it_interval.tv_usec = 0;
98         if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
99             PLOG_E("Couldn't set the ITIMER_REAL timer");
100             return false;
101         }
102 
103         /*
104          * ..if a process sleeps and catches SIGPROF/SIGALRM
105          * rlimits won't help either. However, arch_checkTimeLimit
106          * will send a SIGKILL at tmOut + 2 seconds. That should
107          * do it :)
108          */
109         struct rlimit rl;
110 
111         rl.rlim_cur = hfuzz->tmOut + 1;
112         rl.rlim_max = hfuzz->tmOut + 1;
113         if (setrlimit(RLIMIT_CPU, &rl) == -1) {
114             PLOG_D("Couldn't enforce the RLIMIT_CPU resource limit");
115         }
116     }
117 
118     /*
119      * The address space limit. If big enough - roughly the size of RAM used
120      */
121     if (hfuzz->asLimit) {
122         struct rlimit rl = {
123             .rlim_cur = hfuzz->asLimit * 1024ULL * 1024ULL,
124             .rlim_max = hfuzz->asLimit * 1024ULL * 1024ULL,
125         };
126         if (setrlimit(RLIMIT_AS, &rl) == -1) {
127             PLOG_D("Couldn't enforce the RLIMIT_AS resource limit, ignoring");
128         }
129     }
130 
131     if (hfuzz->nullifyStdio) {
132         util_nullifyStdio();
133     }
134 
135     if (hfuzz->fuzzStdin) {
136         /*
137          * Uglyyyyyy ;)
138          */
139         if (!util_redirectStdin(fileName)) {
140             return false;
141         }
142     }
143 
144     if (hfuzz->clearEnv) {
145         environ = NULL;
146     }
147     if (sancov_prepareExecve(hfuzz) == false) {
148         LOG_E("sancov_prepareExecve() failed");
149         return false;
150     }
151     for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs) && hfuzz->envs[i]; i++) {
152         putenv(hfuzz->envs[i]);
153     }
154     setsid();
155 
156     return true;
157 }
158 
fuzz_prepareFileDynamically(honggfuzz_t * hfuzz,fuzzer_t * fuzzer)159 static bool fuzz_prepareFileDynamically(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
160 {
161     struct dynfile_t *dynfile;
162 
163     {
164         MX_LOCK(&hfuzz->dynfileq_mutex);
165         DEFER(MX_UNLOCK(&hfuzz->dynfileq_mutex));
166 
167         if (hfuzz->dynfileqCnt == 0) {
168             LOG_F("No files in the dynamic queue. The initial phase did not "
169                   "result in positive counters/coverage");
170         }
171 
172         size_t i = 0U;
173         size_t dynFilePos = util_rndGet(0, hfuzz->dynfileqCnt - 1);
174         TAILQ_FOREACH(dynfile, &hfuzz->dynfileq, pointers) {
175             if (i++ == dynFilePos) {
176                 break;
177             }
178         }
179     }
180 
181     memcpy(fuzzer->dynamicFile, dynfile->data, dynfile->size);
182     fuzzer->dynamicFileSz = dynfile->size;
183 
184     mangle_Resize(hfuzz, fuzzer->dynamicFile, &fuzzer->dynamicFileSz);
185     mangle_mangleContent(hfuzz, fuzzer);
186 
187     if (files_writeBufToFile
188         (fuzzer->fileName, fuzzer->dynamicFile, fuzzer->dynamicFileSz,
189          O_WRONLY | O_CREAT | O_EXCL | O_TRUNC) == false) {
190         LOG_E("Couldn't write buffer to file '%s'", fuzzer->fileName);
191         return false;
192     }
193 
194     return true;
195 }
196 
fuzz_prepareFile(honggfuzz_t * hfuzz,fuzzer_t * fuzzer,int rnd_index)197 static bool fuzz_prepareFile(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index)
198 {
199     size_t fileSz =
200         files_readFileToBufMax(hfuzz->files[rnd_index], fuzzer->dynamicFile, hfuzz->maxFileSz);
201     if (fileSz == 0UL) {
202         LOG_E("Couldn't read contents of '%s'", hfuzz->files[rnd_index]);
203         return false;
204     }
205     fuzzer->dynamicFileSz = fileSz;
206 
207     /* If flip rate is 0.0, early abort file mangling */
208     if (fuzzer->flipRate != 0.0L) {
209         mangle_Resize(hfuzz, fuzzer->dynamicFile, &fuzzer->dynamicFileSz);
210         mangle_mangleContent(hfuzz, fuzzer);
211     }
212 
213     if (files_writeBufToFile
214         (fuzzer->fileName, fuzzer->dynamicFile, fuzzer->dynamicFileSz,
215          O_WRONLY | O_CREAT | O_EXCL) == false) {
216         LOG_E("Couldn't write buffer to file '%s'", fuzzer->fileName);
217         return false;
218     }
219 
220     return true;
221 }
222 
fuzz_prepareFileExternally(honggfuzz_t * hfuzz,fuzzer_t * fuzzer,int rnd_index)223 static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, fuzzer_t * fuzzer, int rnd_index)
224 {
225     {
226         int dstfd = open(fuzzer->fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
227         if (dstfd == -1) {
228             PLOG_E("Couldn't create a temporary file '%s'", fuzzer->fileName);
229             return false;
230         }
231         DEFER(close(dstfd));
232 
233         LOG_D("Created '%s' as an input file", fuzzer->fileName);
234 
235         if (hfuzz->inputFile) {
236             size_t fileSz = files_readFileToBufMax(hfuzz->files[rnd_index], fuzzer->dynamicFile,
237                                                    hfuzz->maxFileSz);
238             if (fileSz == 0UL) {
239                 LOG_E("Couldn't read '%s'", hfuzz->files[rnd_index]);
240                 unlink(fuzzer->fileName);
241                 return false;
242             }
243 
244             if (files_writeToFd(dstfd, fuzzer->dynamicFile, fileSz) == false) {
245                 unlink(fuzzer->fileName);
246                 return false;
247             }
248         }
249 
250     }
251 
252     pid_t pid = arch_fork(hfuzz);
253     if (pid == -1) {
254         PLOG_E("Couldn't fork");
255         return false;
256     }
257 
258     if (!pid) {
259         /*
260          * child performs the external file modifications
261          */
262         execl(hfuzz->externalCommand, hfuzz->externalCommand, fuzzer->fileName, NULL);
263         PLOG_F("Couldn't execute '%s %s'", hfuzz->externalCommand, fuzzer->fileName);
264         return false;
265     }
266 
267     /*
268      * parent waits until child is done fuzzing the input file
269      */
270     int childStatus;
271     int flags = 0;
272 #if defined(__WNOTHREAD)
273     flags |= __WNOTHREAD;
274 #endif                          /* defined(__WNOTHREAD) */
275     while (wait4(pid, &childStatus, flags, NULL) != pid) ;
276     if (WIFEXITED(childStatus)) {
277         LOG_D("External command exited with status %d", WEXITSTATUS(childStatus));
278         return true;
279     }
280     if (WIFSIGNALED(childStatus)) {
281         LOG_E("External command terminated with signal %d", WTERMSIG(childStatus));
282         return false;
283     }
284     LOG_F("External command terminated abnormally, status: %d", childStatus);
285     return false;
286 }
287 
fuzz_runVerifier(honggfuzz_t * hfuzz,fuzzer_t * crashedFuzzer)288 static bool fuzz_runVerifier(honggfuzz_t * hfuzz, fuzzer_t * crashedFuzzer)
289 {
290     int crashFd = -1;
291     uint8_t *crashBuf = NULL;
292     off_t crashFileSz = 0;
293 
294     crashBuf = files_mapFile(crashedFuzzer->crashFileName, &crashFileSz, &crashFd, false);
295     if (crashBuf == NULL) {
296         LOG_E("Couldn't open and map '%s' in R/O mode", crashedFuzzer->crashFileName);
297         return false;
298     }
299     DEFER(munmap(crashBuf, crashFileSz));
300     DEFER(close(crashFd));
301 
302     LOG_I("Launching verifier for %" PRIx64 " hash", crashedFuzzer->backtrace);
303     for (int i = 0; i < _HF_VERIFIER_ITER; i++) {
304         fuzzer_t vFuzzer = {
305             .pid = 0,
306             .timeStartedMillis = util_timeNowMillis(),
307             .crashFileName = {0},
308             .pc = 0ULL,
309             .backtrace = 0ULL,
310             .access = 0ULL,
311             .exception = 0,
312             .dynamicFileSz = 0,
313             .dynamicFile = NULL,
314             .hwCnts = {
315                        .cpuInstrCnt = 0ULL,
316                        .cpuBranchCnt = 0ULL,
317                        .customCnt = 0ULL,
318                        .bbCnt = 0ULL,
319                        },
320             .sanCovCnts = {
321                            .hitBBCnt = 0ULL,
322                            .totalBBCnt = 0ULL,
323                            .dsoCnt = 0ULL,
324                            .iDsoCnt = 0ULL,
325                            .newBBCnt = 0ULL,
326                            .crashesCnt = 0ULL,
327                            },
328             .report = {'\0'},
329             .mainWorker = false
330         };
331 
332         fuzz_getFileName(hfuzz, vFuzzer.fileName);
333         if (files_writeBufToFile
334             (vFuzzer.fileName, crashBuf, crashFileSz, O_WRONLY | O_CREAT | O_EXCL) == false) {
335             LOG_E("Couldn't write buffer to file '%s'", vFuzzer.fileName);
336             return false;
337         }
338 
339         vFuzzer.pid = arch_fork(hfuzz);
340         if (vFuzzer.pid == -1) {
341             PLOG_F("Couldn't fork");
342             return false;
343         }
344 
345         if (!vFuzzer.pid) {
346             if (fuzz_prepareExecve(hfuzz, crashedFuzzer->crashFileName) == false) {
347                 LOG_E("fuzz_prepareExecve() failed");
348                 return false;
349             }
350             if (!arch_launchChild(hfuzz, crashedFuzzer->crashFileName)) {
351                 LOG_E("Error launching verifier child process");
352                 return false;
353             }
354         }
355 
356         arch_reapChild(hfuzz, &vFuzzer);
357         unlink(vFuzzer.fileName);
358 
359         /* If stack hash doesn't match skip name tag and exit */
360         if (crashedFuzzer->backtrace != vFuzzer.backtrace) {
361             LOG_D("Verifier stack hash mismatch");
362             return false;
363         }
364     }
365 
366     /* Workspace is inherited, just append a extra suffix */
367     char verFile[PATH_MAX] = { 0 };
368     snprintf(verFile, sizeof(verFile), "%s.verified", crashedFuzzer->crashFileName);
369 
370     /* Copy file with new suffix & remove original copy */
371     bool dstFileExists = false;
372     if (files_copyFile(crashedFuzzer->crashFileName, verFile, &dstFileExists)) {
373         LOG_I("Successfully verified, saving as (%s)", verFile);
374         __sync_fetch_and_add(&hfuzz->verifiedCrashesCnt, 1UL);
375         unlink(crashedFuzzer->crashFileName);
376     } else {
377         if (dstFileExists) {
378             LOG_I("It seems that '%s' already exists, skipping", verFile);
379         } else {
380             LOG_E("Couldn't copy '%s' to '%s'", crashedFuzzer->crashFileName, verFile);
381             return false;
382         }
383     }
384 
385     return true;
386 }
387 
fuzz_addFileToFileQLocked(honggfuzz_t * hfuzz,uint8_t * data,size_t size)388 static void fuzz_addFileToFileQLocked(honggfuzz_t * hfuzz, uint8_t * data, size_t size)
389 {
390     struct dynfile_t *dynfile = (struct dynfile_t *)util_Malloc(sizeof(struct dynfile_t));
391     dynfile->size = size;
392     dynfile->data = (uint8_t *) util_Malloc(size);
393     memcpy(dynfile->data, data, size);
394     TAILQ_INSERT_TAIL(&hfuzz->dynfileq, dynfile, pointers);
395     hfuzz->dynfileqCnt++;
396 
397     char fname[PATH_MAX];
398     snprintf(fname, sizeof(fname), "%s/COVERAGE_DATA.PID.%d.RND.%" PRIu64 ".%" PRIx64,
399              hfuzz->workDir, getpid(), (uint64_t) time(NULL), util_rndGet(0, 0xFFFFFFFFFFFF));
400     if (files_writeBufToFile(fname, data, size, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC) == false) {
401         LOG_W("Couldn't write buffer to file '%s'", fname);
402     }
403 }
404 
fuzz_perfFeedback(honggfuzz_t * hfuzz,fuzzer_t * fuzzer)405 static void fuzz_perfFeedback(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
406 {
407     LOG_D
408         ("New file size: %zu, Perf feedback new/cur (instr,branch): %" PRIu64 "/%" PRIu64 ",%"
409          PRIu64 "/%" PRIu64 ", BBcnt new/total: %" PRIu64 "/%" PRIu64, fuzzer->dynamicFileSz,
410          fuzzer->hwCnts.cpuInstrCnt, hfuzz->hwCnts.cpuInstrCnt, fuzzer->hwCnts.cpuBranchCnt,
411          hfuzz->hwCnts.cpuBranchCnt, fuzzer->hwCnts.bbCnt, hfuzz->hwCnts.bbCnt);
412 
413     MX_LOCK(&hfuzz->dynfileq_mutex);
414     DEFER(MX_UNLOCK(&hfuzz->dynfileq_mutex));
415 
416     int64_t diff0 = hfuzz->hwCnts.cpuInstrCnt - fuzzer->hwCnts.cpuInstrCnt;
417     int64_t diff1 = hfuzz->hwCnts.cpuBranchCnt - fuzzer->hwCnts.cpuBranchCnt;
418     int64_t diff2 = hfuzz->hwCnts.customCnt - fuzzer->hwCnts.customCnt;
419 
420     if (diff0 < 0 || diff1 < 0 || diff2 < 0 || fuzzer->hwCnts.bbCnt > 0) {
421         LOG_I
422             ("New file size: %zu, Perf feedback new/cur (instr,branch): %" PRIu64 "/%" PRIu64 ",%"
423              PRIu64 "/%" PRIu64 ", BBcnt new/total: %" PRIu64 "/%" PRIu64, fuzzer->dynamicFileSz,
424              fuzzer->hwCnts.cpuInstrCnt, hfuzz->hwCnts.cpuInstrCnt, fuzzer->hwCnts.cpuBranchCnt,
425              hfuzz->hwCnts.cpuBranchCnt, fuzzer->hwCnts.bbCnt, hfuzz->hwCnts.bbCnt);
426 
427         hfuzz->hwCnts.cpuInstrCnt = fuzzer->hwCnts.cpuInstrCnt;
428         hfuzz->hwCnts.cpuBranchCnt = fuzzer->hwCnts.cpuBranchCnt;
429         hfuzz->hwCnts.customCnt = fuzzer->hwCnts.customCnt;
430         hfuzz->hwCnts.bbCnt += fuzzer->hwCnts.bbCnt;
431 
432         fuzz_addFileToFileQLocked(hfuzz, fuzzer->dynamicFile, fuzzer->dynamicFileSz);
433     }
434 }
435 
fuzz_sanCovFeedback(honggfuzz_t * hfuzz,fuzzer_t * fuzzer)436 static void fuzz_sanCovFeedback(honggfuzz_t * hfuzz, fuzzer_t * fuzzer)
437 {
438     LOG_D
439         ("File size (Best/New): %zu, SanCov feedback (bb,dso): Best: [%" PRIu64
440          ",%" PRIu64 "] / New: [%" PRIu64 ",%" PRIu64 "], newBBs:%" PRIu64,
441          fuzzer->dynamicFileSz, hfuzz->sanCovCnts.hitBBCnt,
442          hfuzz->sanCovCnts.iDsoCnt, fuzzer->sanCovCnts.hitBBCnt, fuzzer->sanCovCnts.iDsoCnt,
443          fuzzer->sanCovCnts.newBBCnt);
444 
445     MX_LOCK(&hfuzz->dynfileq_mutex);
446     DEFER(MX_UNLOCK(&hfuzz->dynfileq_mutex));
447 
448     /*
449      * Keep mutated seed if:
450      *  a) Newly discovered (not met before) BBs
451      *  b) More instrumented DSOs loaded
452      *
453      * TODO: (a) method can significantly assist to further improvements in interesting areas
454      * discovery if combined with seeds pool/queue support. If a runtime queue is maintained
455      * more interesting seeds can be saved between runs instead of instantly discarded
456      * based on current absolute elitism (only one mutated seed is promoted).
457      */
458     if (fuzzer->sanCovCnts.newBBCnt > 0 || hfuzz->sanCovCnts.iDsoCnt < fuzzer->sanCovCnts.iDsoCnt) {
459         LOG_I("SanCov Update: file size (Cur): %zu, newBBs:%" PRIu64
460               ", counters (Cur,New): %" PRIu64 "/%" PRIu64 ",%" PRIu64 "/%" PRIu64,
461               fuzzer->dynamicFileSz, fuzzer->sanCovCnts.newBBCnt,
462               hfuzz->sanCovCnts.hitBBCnt, hfuzz->sanCovCnts.iDsoCnt, fuzzer->sanCovCnts.hitBBCnt,
463               fuzzer->sanCovCnts.iDsoCnt);
464 
465         hfuzz->sanCovCnts.hitBBCnt += fuzzer->sanCovCnts.newBBCnt;
466         hfuzz->sanCovCnts.dsoCnt = fuzzer->sanCovCnts.dsoCnt;
467         hfuzz->sanCovCnts.iDsoCnt = fuzzer->sanCovCnts.iDsoCnt;
468         hfuzz->sanCovCnts.crashesCnt += fuzzer->sanCovCnts.crashesCnt;
469         hfuzz->sanCovCnts.newBBCnt = fuzzer->sanCovCnts.newBBCnt;
470 
471         if (hfuzz->sanCovCnts.totalBBCnt < fuzzer->sanCovCnts.totalBBCnt) {
472             /* Keep only the max value (for dlopen cases) to measure total target coverage */
473             hfuzz->sanCovCnts.totalBBCnt = fuzzer->sanCovCnts.totalBBCnt;
474         }
475 
476         fuzz_addFileToFileQLocked(hfuzz, fuzzer->dynamicFile, fuzzer->dynamicFileSz);
477     }
478 }
479 
fuzz_getState(honggfuzz_t * hfuzz)480 static fuzzState_t fuzz_getState(honggfuzz_t * hfuzz)
481 {
482     return __sync_fetch_and_add(&hfuzz->state, 0UL);
483 }
484 
fuzz_setState(honggfuzz_t * hfuzz,fuzzState_t state)485 static void fuzz_setState(honggfuzz_t * hfuzz, fuzzState_t state)
486 {
487     __sync_lock_test_and_set(&hfuzz->state, state);
488 }
489 
fuzz_fuzzLoop(honggfuzz_t * hfuzz)490 static void fuzz_fuzzLoop(honggfuzz_t * hfuzz)
491 {
492     fuzzer_t fuzzer = {
493         .pid = 0,
494         .timeStartedMillis = util_timeNowMillis(),
495         .crashFileName = {0},
496         .pc = 0ULL,
497         .backtrace = 0ULL,
498         .access = 0ULL,
499         .exception = 0,
500         .mainWorker = true,
501         .flipRate = hfuzz->origFlipRate,
502 
503         .sanCovCnts = {
504                        .hitBBCnt = 0ULL,
505                        .totalBBCnt = 0ULL,
506                        .dsoCnt = 0ULL,
507                        .iDsoCnt = 0ULL,
508                        .newBBCnt = 0ULL,
509                        .crashesCnt = 0ULL,
510                        },
511         .dynamicFileSz = 0,
512         .dynamicFile = util_Malloc(hfuzz->maxFileSz),
513 
514         .hwCnts = {
515                    .cpuInstrCnt = 0ULL,
516                    .cpuBranchCnt = 0ULL,
517                    .customCnt = 0ULL,
518                    .bbCnt = 0ULL,
519                    },
520         .report = {'\0'},
521     };
522     DEFER(free(fuzzer.dynamicFile));
523 
524     size_t rnd_index = util_rndGet(0, hfuzz->fileCnt - 1);
525 
526     /* If dry run mode, pick the next file and not a random one */
527     if (fuzzer.flipRate == 0.0L && hfuzz->useVerifier) {
528         rnd_index = __sync_fetch_and_add(&hfuzz->lastFileIndex, 1UL);
529     }
530 
531     if (fuzz_getState(hfuzz) == _HF_STATE_DYNAMIC_PRE) {
532         rnd_index = __sync_fetch_and_add(&hfuzz->lastFileIndex, 1UL);
533         if (rnd_index >= hfuzz->fileCnt) {
534             while (fuzz_getState(hfuzz) == _HF_STATE_DYNAMIC_PRE) {
535                 sleep(1);
536             }
537         }
538     }
539 
540     if (hfuzz->state == _HF_STATE_DYNAMIC_MAIN) {
541         strncpy(fuzzer.origFileName, "DYNAMIC", PATH_MAX);
542     } else {
543         strncpy(fuzzer.origFileName, files_basename(hfuzz->files[rnd_index]), PATH_MAX);
544     }
545 
546     fuzzState_t state = fuzz_getState(hfuzz);
547     fuzz_getFileName(hfuzz, fuzzer.fileName);
548 
549     if (state == _HF_STATE_DYNAMIC_MAIN) {
550         if (!fuzz_prepareFileDynamically(hfuzz, &fuzzer)) {
551             exit(EXIT_FAILURE);
552         }
553     } else if (state == _HF_STATE_DYNAMIC_PRE) {
554         fuzzer.flipRate = 0.0f;
555         if (!fuzz_prepareFile(hfuzz, &fuzzer, rnd_index)) {
556             exit(EXIT_FAILURE);
557         }
558     } else if (hfuzz->externalCommand != NULL) {
559         if (!fuzz_prepareFileExternally(hfuzz, &fuzzer, rnd_index)) {
560             exit(EXIT_FAILURE);
561         }
562     } else {
563         if (!fuzz_prepareFile(hfuzz, &fuzzer, rnd_index)) {
564             exit(EXIT_FAILURE);
565         }
566     }
567 
568     fuzzer.pid = arch_fork(hfuzz);
569     if (fuzzer.pid == -1) {
570         PLOG_F("Couldn't fork");
571         exit(EXIT_FAILURE);
572     }
573 
574     if (!fuzzer.pid) {
575         if (!fuzz_prepareExecve(hfuzz, fuzzer.fileName)) {
576             LOG_E("fuzz_prepareExecve() failed");
577             exit(EXIT_FAILURE);
578         }
579         if (!arch_launchChild(hfuzz, fuzzer.fileName)) {
580             LOG_E("Error launching child process");
581             exit(EXIT_FAILURE);
582         }
583     }
584 
585     LOG_D("Launched new process, pid: %d, (concurrency: %zd)", fuzzer.pid, hfuzz->threadsMax);
586 
587     arch_reapChild(hfuzz, &fuzzer);
588     unlink(fuzzer.fileName);
589 
590     if (hfuzz->dynFileMethod != _HF_DYNFILE_NONE) {
591         fuzz_perfFeedback(hfuzz, &fuzzer);
592     } else if (hfuzz->useSanCov) {
593         fuzz_sanCovFeedback(hfuzz, &fuzzer);
594     }
595 
596     if (hfuzz->useVerifier && (fuzzer.crashFileName[0] != 0) && fuzzer.backtrace) {
597         if (!fuzz_runVerifier(hfuzz, &fuzzer)) {
598             LOG_I("Failed to verify %s", fuzzer.crashFileName);
599         }
600     }
601 
602     report_Report(hfuzz, fuzzer.report);
603 
604     if (state == _HF_STATE_DYNAMIC_PRE
605         && __sync_add_and_fetch(&hfuzz->doneFileIndex, 1UL) >= hfuzz->fileCnt) {
606         fuzz_setState(hfuzz, _HF_STATE_DYNAMIC_MAIN);
607     }
608 }
609 
fuzz_threadNew(void * arg)610 static void *fuzz_threadNew(void *arg)
611 {
612     honggfuzz_t *hfuzz = (honggfuzz_t *) arg;
613 
614     for (;;) {
615         /* Check if dry run mode with verifier enabled */
616         if (hfuzz->origFlipRate == 0.0L && hfuzz->useVerifier) {
617             if (__sync_fetch_and_add(&hfuzz->mutationsCnt, 1UL) >= hfuzz->fileCnt) {
618                 __sync_fetch_and_add(&hfuzz->threadsFinished, 1UL);
619                 // All files checked, weak-up the main process
620                 pthread_kill(fuzz_mainThread, SIGALRM);
621                 return NULL;
622             }
623         }
624         /* Check for max iterations limit if set */
625         else if ((__sync_fetch_and_add(&hfuzz->mutationsCnt, 1UL) >= hfuzz->mutationsMax)
626                  && hfuzz->mutationsMax) {
627             __sync_fetch_and_add(&hfuzz->threadsFinished, 1UL);
628             // Wake-up the main process
629             pthread_kill(fuzz_mainThread, SIGALRM);
630             return NULL;
631         }
632 
633         fuzz_fuzzLoop(hfuzz);
634     }
635 }
636 
fuzz_runThread(honggfuzz_t * hfuzz,void * (* thread)(void *))637 static void fuzz_runThread(honggfuzz_t * hfuzz, void *(*thread) (void *))
638 {
639     pthread_attr_t attr;
640 
641     pthread_attr_init(&attr);
642     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
643     pthread_attr_setstacksize(&attr, _HF_PTHREAD_STACKSIZE);
644     pthread_attr_setguardsize(&attr, (size_t) sysconf(_SC_PAGESIZE));
645 
646     pthread_t t;
647     if (pthread_create(&t, &attr, thread, (void *)hfuzz) < 0) {
648         PLOG_F("Couldn't create a new thread");
649     }
650 
651     return;
652 }
653 
fuzz_threads(honggfuzz_t * hfuzz)654 void fuzz_threads(honggfuzz_t * hfuzz)
655 {
656     fuzz_mainThread = pthread_self();
657 
658     if (!arch_archInit(hfuzz)) {
659         LOG_F("Couldn't prepare arch for fuzzing");
660     }
661     if (!sancov_Init(hfuzz)) {
662         LOG_F("Couldn't prepare sancov options");
663     }
664 
665     if (hfuzz->useSanCov || hfuzz->dynFileMethod != _HF_DYNFILE_NONE) {
666         hfuzz->state = _HF_STATE_DYNAMIC_PRE;
667     } else {
668         hfuzz->state = _HF_STATE_STATIC;
669     }
670 
671     for (size_t i = 0; i < hfuzz->threadsMax; i++) {
672         fuzz_runThread(hfuzz, fuzz_threadNew);
673     }
674 }
675