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