1 /* 2 * 3 * honggfuzz - sanitizer coverage feedback parsing 4 * ----------------------------------------------- 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); you may 7 * not use this file except in compliance with the License. You may obtain 8 * a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 * implied. See the License for the specific language governing 16 * permissions and limitations under the License. 17 * 18 */ 19 20 /* 21 * Clang sanitizer coverage (sancov) data parsing functions. Supported methods: 22 * - raw unified data (preferred method) 23 * - individual data per executable/DSO (not preferred since lots of data lost if instrumented 24 * code exits abnormally or with sanitizer unhandled signal (common in Android OS) 25 * 26 * For raw-unpack method a global (shared across workers) Trie is created for the chosen 27 * initial seed and maintained until seed is replaced. Trie nodes store the loaded (as exposed 28 * from *.sancov.map file) execs/DSOs from target application using the map name as key. Trie node 29 * data struct (trieData_t) maintains information for each instrumented map including a bitmap with 30 * all hit relative BB addresses (realBBAddr - baseAddr to circumvent ASLR). Map's bitmap is updated 31 * while new areas on target application are discovered based on absolute elitism implemented at 32 * fuzz_sanCovFeedback(). 33 * 34 * For individual data files a pid (fuzzer's thread or remote process) based filename search is 35 * performed to identify all files belonging to examined execution. This method doesn't implement 36 * yet bitmap runtime data to detect newly discovered areas. It's mainly used so far as a comparison 37 * metric for raw-unpack method and stability check for sancov experimental features such as 38 * coverage counters: http://clang.llvm.org/docs/SanitizerCoverage.html 39 */ 40 41 #include "sancov.h" 42 43 #include <ctype.h> 44 #include <dirent.h> 45 #include <inttypes.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <sys/mman.h> 50 #include <sys/stat.h> 51 #include <sys/types.h> 52 53 #include "libcommon/common.h" 54 #include "libcommon/files.h" 55 #include "libcommon/log.h" 56 #include "libcommon/util.h" 57 #include "sanitizers.h" 58 59 /* sancov files magic values */ 60 #define kMagic32 0xC0BFFFFFFFFFFF32 61 #define kMagic64 0xC0BFFFFFFFFFFF64 62 63 /* 64 * Each DSO/executable that has been compiled with enabled coverage instrumentation 65 * is detected from compiler_rt runtime library when loaded. When coverage_direct 66 * method is selected, runtime library is pre-allocating kPcArrayMmapSize [1] byte 67 * chunks until the total size of chunks is greater than the number of inserted 68 * guards. This effectively means that we might have a large unused (zero-filled) 69 * area that we can't identify at runtime (we need to do binary inspection). 70 * 71 * Runtime maintained data structs size overhead is not affected since fixed-size 72 * bitmap is used. However, the way the display coverage statistics are generated 73 * is not very accurate because: 74 * a) ASan compiled DSO might get loaded although not followed from monitoring 75 execution affecting the counters 76 * b) Not all zero-fill chunks translate into non-hit basic block as they might 77 * be the chunk padding 78 * 79 * Probably there aren't many we can do to deal with this issue without introducing 80 * a huge performance overhead at an already costly feedback method. 81 * 82 * [1] 83 'https://llvm.org/svn/llvm-project/compiler-rt/branches/release_38/lib/sanitizer_common/sanitizer_coverage_libcdep.cc' 84 */ 85 #define kPcArrayMmapSize (64 * 1024) 86 87 /* 88 * bitmap implementation 89 */ 90 static bitmap_t* sancov_newBitmap(uint32_t capacity) { 91 bitmap_t* pBM = util_Malloc(sizeof(bitmap_t)); 92 pBM->capacity = capacity; 93 pBM->nChunks = (capacity + 31) / 32; 94 pBM->pChunks = util_Calloc(pBM->nChunks * sizeof(uint32_t)); 95 return pBM; 96 } 97 98 static inline bool sancov_queryBitmap(bitmap_t* pBM, uint32_t index) { 99 if (index > pBM->capacity) { 100 LOG_E("bitmap overflow (%u)", index); 101 return false; 102 } 103 if (pBM->pChunks[index / 32] & (1 << (index % 32))) { 104 return true; 105 } 106 return false; 107 } 108 109 static inline void sancov_setBitmap(bitmap_t* pBM, uint32_t index) { 110 /* This will be removed. So far checks only to verify accepted ranges. */ 111 if (index >= pBM->capacity) { 112 LOG_E("Out of range index (%u > %u)", index, pBM->capacity); 113 } 114 pBM->pChunks[index / 32] |= (1 << (index % 32)); 115 } 116 117 static inline void sancov_destroyBitmap(bitmap_t* pBM) { 118 free(pBM->pChunks); 119 free(pBM); 120 } 121 122 /* 123 * Trie implementation 124 */ 125 static node_t* sancov_trieCreateNode(char key) { 126 node_t* node = (node_t*)util_Malloc(sizeof(node_t)); 127 node->key = key; 128 node->next = NULL; 129 node->children = NULL; 130 node->parent = NULL; 131 node->prev = NULL; 132 133 /* Zero init node's data struct */ 134 memset(&node->data, 0, sizeof(trieData_t)); 135 return node; 136 } 137 138 static node_t* sancov_trieSearch(node_t* root, const char* key) { 139 node_t *pNodeLevel = root, *pNodePtr = NULL; 140 int nodeLevelId = 0; 141 while (1) { 142 node_t *pNodeFound = NULL, *pCurNode = NULL; 143 for (pCurNode = pNodeLevel; pCurNode != NULL; pCurNode = pCurNode->next) { 144 if (pCurNode->key == *key) { 145 pNodeFound = pCurNode; 146 nodeLevelId++; 147 break; 148 } 149 } 150 if (pNodeFound == NULL) { 151 return NULL; 152 } 153 if (*key == '\0') { 154 pNodePtr = pCurNode; 155 return pNodePtr; 156 } 157 pNodeLevel = pNodeFound->children; 158 key++; 159 } 160 } 161 162 static void sancov_trieAdd(node_t** root, const char* key) { 163 if (*root == NULL) { 164 LOG_E("Invalid Trie (NULL root node)"); 165 return; 166 } 167 168 /* Traverse Trie */ 169 node_t* pTravNode = (*root)->children; 170 if (pTravNode == NULL) { 171 /* First node */ 172 for (pTravNode = *root; *key != '\0'; pTravNode = pTravNode->children) { 173 pTravNode->children = sancov_trieCreateNode(*key); 174 pTravNode->children->parent = pTravNode; 175 key++; 176 } 177 pTravNode->children = sancov_trieCreateNode('\0'); 178 pTravNode->children->parent = pTravNode; 179 return; 180 } 181 182 while (*key != '\0') { 183 if (*key == pTravNode->key) { 184 key++; 185 pTravNode = pTravNode->children; 186 } else { 187 break; 188 } 189 } 190 while (pTravNode->next) { 191 if (*key == pTravNode->next->key) { 192 key++; 193 sancov_trieAdd(&(pTravNode->next), key); 194 return; 195 } 196 pTravNode = pTravNode->next; 197 } 198 if (*key) { 199 pTravNode->next = sancov_trieCreateNode(*key); 200 } else { 201 pTravNode->next = sancov_trieCreateNode(*key); 202 } 203 pTravNode->next->parent = pTravNode->parent; 204 pTravNode->next->prev = pTravNode; 205 if (!*key) { 206 return; 207 } 208 key++; 209 for (pTravNode = pTravNode->next; *key; pTravNode = pTravNode->children) { 210 pTravNode->children = sancov_trieCreateNode(*key); 211 pTravNode->children->parent = pTravNode; 212 key++; 213 } 214 pTravNode->children = sancov_trieCreateNode('\0'); 215 pTravNode->children->parent = pTravNode; 216 217 return; 218 } 219 220 static inline void sancov_trieFreeNode(node_t* node) { 221 /* First destroy bitmap heap buffers allocated for instrumented maps */ 222 if (node->data.pBM) { 223 sancov_destroyBitmap(node->data.pBM); 224 } 225 free(node); 226 } 227 228 static inline void sancov_trieCreate(node_t** root) { 229 /* Create root node if new Trie */ 230 *root = sancov_trieCreateNode('\0'); 231 } 232 233 /* Destroy Trie - iterate nodes and free memory */ 234 UNUSED static void sancov_trieDestroy(node_t* root) { 235 node_t* pNode = root; 236 node_t* pNodeTmp = root; 237 while (pNode) { 238 while (pNode->children) { 239 pNode = pNode->children; 240 } 241 242 if (pNode->prev && pNode->next) { 243 pNodeTmp = pNode; 244 pNode->next->prev = pNode->prev; 245 pNode->prev->next = pNode->next; 246 sancov_trieFreeNode(pNodeTmp); 247 } else if (pNode->prev && !pNode->next) { 248 pNodeTmp = pNode; 249 pNode->prev->next = NULL; 250 sancov_trieFreeNode(pNodeTmp); 251 } else if (!pNode->prev && pNode->next) { 252 pNodeTmp = pNode; 253 pNode->parent->children = pNode->next; 254 pNode->next->prev = NULL; 255 pNode = pNode->next; 256 sancov_trieFreeNode(pNodeTmp); 257 } else { 258 pNodeTmp = pNode; 259 if (pNode->parent == NULL) { 260 /* Root */ 261 sancov_trieFreeNode(pNodeTmp); 262 return; 263 } 264 pNode = pNode->parent; 265 pNode->children = NULL; 266 sancov_trieFreeNode(pNodeTmp); 267 } 268 } 269 } 270 271 /* Modified interpolation search algorithm to search for nearest address fit */ 272 static inline uint64_t sancov_interpSearch(uint64_t* buf, uint64_t size, uint64_t key) { 273 /* Avoid extra checks assuming caller always provides non-zero array size */ 274 uint64_t low = 0; 275 uint64_t high = size - 1; 276 uint64_t mid = high; 277 278 while (buf[high] != buf[low] && key >= buf[low] && key <= buf[high]) { 279 mid = low + (key - buf[low]) * ((high - low) / (buf[high] - buf[low])); 280 if (buf[mid] < key) { 281 low = mid + 1; 282 } else if (key < buf[mid]) { 283 high = mid - 1; 284 } else { 285 return mid; 286 } 287 } 288 return mid; 289 } 290 291 /* qsort struct comparison function (memMap_t struct start addr field) */ 292 static int sancov_qsortCmp(const void* a, const void* b) { 293 memMap_t* pA = (memMap_t*)a; 294 memMap_t* pB = (memMap_t*)b; 295 if (pA->start < pB->start) { 296 return -1; 297 } else if (pA->start > pB->start) { 298 return 1; 299 } else { 300 /* Normally we should never hit that case */ 301 LOG_W("Duplicate map start addr detected"); 302 return 0; 303 } 304 } 305 306 static bool sancov_sanCovParseRaw(run_t* run) { 307 int dataFd = -1; 308 uint8_t* dataBuf = NULL; 309 off_t dataFileSz = 0, pos = 0; 310 bool is32bit = true; 311 char covFile[PATH_MAX] = {0}; 312 pid_t targetPid = (run->global->linux.pid > 0) ? run->global->linux.pid : run->pid; 313 314 /* Fuzzer local runtime data structs - need free() before exit */ 315 uint64_t* startMapsIndex = NULL; 316 memMap_t* mapsBuf = NULL; 317 318 /* Local counters */ 319 uint64_t nBBs = 0; /* Total BB hits found in raw file */ 320 uint64_t nZeroBBs = 0; /* Number of non-hit instrumented BBs */ 321 uint64_t mapsNum = 0; /* Total number of entries in map file */ 322 uint64_t noCovMapsNum = 0; /* Loaded DSOs not compiled with coverage */ 323 324 /* File line-by-line read help buffers */ 325 __block char* pLine = NULL; 326 size_t lineSz = 0; 327 328 /* Coverage data analysis starts by parsing map file listing */ 329 snprintf(covFile, sizeof(covFile), "%s/%s/%d.sancov.map", run->global->io.workDir, 330 _HF_SANCOV_DIR, targetPid); 331 if (!files_exists(covFile)) { 332 LOG_D("sancov map file not found"); 333 return false; 334 } 335 FILE* fCovMap = fopen(covFile, "rb"); 336 if (fCovMap == NULL) { 337 PLOG_E("Couldn't open '%s' - R/O mode", covFile); 338 return false; 339 } 340 defer { fclose(fCovMap); }; 341 342 /* First line contains PC length (32/64-bit) */ 343 if (getline(&pLine, &lineSz, fCovMap) == -1) { 344 LOG_E("Invalid map file '%s'", covFile); 345 return false; 346 } 347 defer { 348 free(pLine); 349 pLine = NULL; 350 }; 351 352 int pcLen = atoi(pLine); 353 if (pcLen == 32) { 354 is32bit = true; 355 } else if (pcLen == 64) { 356 is32bit = false; 357 } else { 358 LOG_E("Invalid PC length (%d) in map file '%s'", pcLen, covFile); 359 } 360 361 /* See if #maps is available from previous run to avoid realloc inside loop */ 362 uint64_t prevMapsNum = ATOMIC_GET(run->global->sanCovCnts.dsoCnt); 363 if (prevMapsNum > 0) { 364 mapsBuf = util_Malloc(prevMapsNum * sizeof(memMap_t)); 365 } 366 /* It's OK to free(NULL) */ 367 defer { free(mapsBuf); }; 368 369 /* Iterate map entries */ 370 for (;;) { 371 if (getline(&pLine, &lineSz, fCovMap) == -1) { 372 break; 373 } 374 375 /* Trim trailing whitespaces, not sure if needed copied from upstream sancov.py */ 376 char* lineEnd = pLine + strlen(pLine) - 1; 377 while (lineEnd > pLine && isspace((int)*lineEnd)) { 378 lineEnd--; 379 } 380 *(lineEnd + 1) = 0; 381 382 /* 383 * Each line has following format: 384 * Start End Base bin/DSO name 385 * b5843000 b584e6ac b5843000 liblog.so 386 */ 387 memMap_t mapData = {.start = 0}; 388 char* savePtr = NULL; 389 mapData.start = strtoull(strtok_r(pLine, " ", &savePtr), NULL, 16); 390 mapData.end = strtoull(strtok_r(NULL, " ", &savePtr), NULL, 16); 391 mapData.base = strtoull(strtok_r(NULL, " ", &savePtr), NULL, 16); 392 char* mapName = strtok_r(NULL, " ", &savePtr); 393 memcpy(mapData.mapName, mapName, strlen(mapName)); 394 395 /* Interaction with global Trie should mutex wrap to avoid threads races */ 396 { 397 MX_SCOPED_LOCK(&run->global->sanCov_mutex); 398 399 /* Add entry to Trie with zero data if not already */ 400 if (!sancov_trieSearch(run->global->covMetadata->children, mapData.mapName)) { 401 sancov_trieAdd(&run->global->covMetadata, mapData.mapName); 402 } 403 } 404 405 /* If no DSO number history (first run) or new DSO loaded, realloc local maps metadata buf 406 */ 407 if (prevMapsNum == 0 || prevMapsNum < mapsNum) { 408 if ((mapsBuf = util_Realloc(mapsBuf, (size_t)(mapsNum + 1) * sizeof(memMap_t))) == 409 NULL) { 410 PLOG_E("realloc failed (sz=%" PRIu64 ")", (mapsNum + 1) * sizeof(memMap_t)); 411 return false; 412 } 413 } 414 415 /* Add entry to local maps metadata array */ 416 memcpy(&mapsBuf[mapsNum], &mapData, sizeof(memMap_t)); 417 418 /* Increase loaded maps counter (includes non-instrumented DSOs too) */ 419 mapsNum++; 420 } 421 422 /* Delete .sancov.map file */ 423 if (run->global->linux.pid == 0 && run->global->persistent == false) { 424 unlink(covFile); 425 } 426 427 /* Create a quick index array with maps start addresses */ 428 startMapsIndex = util_Malloc(mapsNum * sizeof(uint64_t)); 429 defer { free(startMapsIndex); }; 430 431 /* Sort quick maps index */ 432 qsort(mapsBuf, mapsNum, sizeof(memMap_t), sancov_qsortCmp); 433 for (size_t i = 0; i < mapsNum; i++) { 434 startMapsIndex[i] = mapsBuf[i].start; 435 } 436 437 /* mmap() .sancov.raw file */ 438 snprintf(covFile, sizeof(covFile), "%s/%s/%d.sancov.raw", run->global->io.workDir, 439 _HF_SANCOV_DIR, targetPid); 440 dataBuf = files_mapFile(covFile, &dataFileSz, &dataFd, false); 441 if (dataBuf == NULL) { 442 LOG_E("Couldn't open and map '%s' in R/O mode", covFile); 443 return false; 444 } 445 defer { 446 munmap(dataBuf, dataFileSz); 447 close(dataFd); 448 }; 449 450 /* 451 * Avoid cost of size checks inside raw data read loop by defining the read function 452 * & pivot size based on PC length. 453 */ 454 uint64_t (*pReadRawBBAddrFunc)(const uint8_t*) = NULL; 455 uint8_t pivot = 0; 456 if (is32bit) { 457 pReadRawBBAddrFunc = &util_getUINT32; 458 pivot = 4; 459 } else { 460 pReadRawBBAddrFunc = &util_getUINT64; 461 pivot = 8; 462 } 463 464 /* 465 * Take advantage of data locality (next processed addr is very likely to belong 466 * to same map) to avoid Trie node search for each read entry. 467 */ 468 node_t* curMap = NULL; 469 uint64_t prevIndex = 0; 470 471 /* Iterate over data buffer containing list of hit BB addresses */ 472 while (pos < dataFileSz) { 473 uint64_t bbAddr = pReadRawBBAddrFunc(dataBuf + pos); 474 pos += pivot; 475 /* Don't bother for zero BB addr (inserted checks without hit) */ 476 if (bbAddr == 0x0) { 477 nZeroBBs++; 478 continue; 479 } else { 480 /* Find best hit based on start addr & verify range for errors */ 481 uint64_t bestFit = sancov_interpSearch(startMapsIndex, mapsNum, bbAddr); 482 if (bbAddr >= mapsBuf[bestFit].start && bbAddr < mapsBuf[bestFit].end) { 483 /* Increase exe/DSO total BB counter */ 484 mapsBuf[bestFit].bbCnt++; 485 486 /* Update current Trie node if map changed */ 487 if (curMap == NULL || (prevIndex != bestFit)) { 488 prevIndex = bestFit; 489 490 /* Interaction with global Trie should mutex wrap to avoid threads races */ 491 { 492 MX_SCOPED_LOCK(&run->global->sanCov_mutex); 493 494 curMap = sancov_trieSearch( 495 run->global->covMetadata->children, mapsBuf[bestFit].mapName); 496 if (curMap == NULL) { 497 LOG_E("Corrupted Trie - '%s' not found", mapsBuf[bestFit].mapName); 498 continue; 499 } 500 501 /* Maintain bitmaps only for exec/DSOs with coverage enabled - allocate on 502 * first use */ 503 if (curMap->data.pBM == NULL) { 504 LOG_D("Allocating bitmap for map '%s'", mapsBuf[bestFit].mapName); 505 curMap->data.pBM = sancov_newBitmap(_HF_SANCOV_BITMAP_SIZE); 506 507 /* 508 * If bitmap allocation failed, unset cached Trie node ptr 509 * to execute this selection branch again. 510 */ 511 if (curMap->data.pBM == NULL) { 512 curMap = NULL; 513 continue; 514 } 515 } 516 } 517 } 518 519 /* If new relative BB addr update DSO's bitmap */ 520 uint32_t relAddr = (uint32_t)(bbAddr - mapsBuf[bestFit].base); 521 if (!sancov_queryBitmap(curMap->data.pBM, relAddr)) { 522 /* Interaction with global Trie should mutex wrap to avoid threads races */ 523 { 524 MX_SCOPED_LOCK(&run->global->sanCov_mutex); 525 526 sancov_setBitmap(curMap->data.pBM, relAddr); 527 } 528 529 /* Also increase new BBs counter at worker's thread runtime data */ 530 mapsBuf[bestFit].newBBCnt++; 531 } 532 } else { 533 /* 534 * Normally this should never get executed. If hit, sanitizer 535 * coverage data collection come across some kind of bug. 536 */ 537 LOG_E("Invalid BB addr (%#" PRIx64 ") at offset %" PRId64, bbAddr, (uint64_t)pos); 538 } 539 } 540 nBBs++; 541 } 542 543 /* Finally iterate over all instrumented maps to sum-up the number of newly met BB addresses */ 544 for (uint64_t i = 0; i < mapsNum; i++) { 545 if (mapsBuf[i].bbCnt > 0) { 546 run->sanCovCnts.newBBCnt += mapsBuf[i].newBBCnt; 547 } else { 548 noCovMapsNum++; 549 } 550 } 551 552 /* Successful parsing - update fuzzer worker's counters */ 553 run->sanCovCnts.hitBBCnt = nBBs; 554 run->sanCovCnts.totalBBCnt = nBBs + nZeroBBs; 555 run->sanCovCnts.dsoCnt = mapsNum; 556 run->sanCovCnts.iDsoCnt = mapsNum - noCovMapsNum; /* Instrumented DSOs */ 557 558 if (run->global->linux.pid == 0 && run->global->persistent == false) { 559 unlink(covFile); 560 } 561 return true; 562 } 563 564 static bool sancov_sanCovParse(run_t* run) { 565 int dataFd = -1; 566 uint8_t* dataBuf = NULL; 567 off_t dataFileSz = 0, pos = 0; 568 bool is32bit = true; 569 char covFile[PATH_MAX] = {0}; 570 DIR* pSanCovDir = NULL; 571 pid_t targetPid = (run->global->linux.pid > 0) ? run->global->linux.pid : run->pid; 572 573 snprintf(covFile, sizeof(covFile), "%s/%s/%s.%d.sancov", run->global->io.workDir, 574 _HF_SANCOV_DIR, files_basename(run->global->exe.cmdline[0]), targetPid); 575 if (!files_exists(covFile)) { 576 LOG_D("Target sancov file not found"); 577 return false; 578 } 579 580 /* Local cache file suffix to use for file search of target pid data */ 581 char pidFSuffix[13] = {0}; 582 snprintf(pidFSuffix, sizeof(pidFSuffix), "%d.sancov", targetPid); 583 584 /* Total BBs counter summarizes all DSOs */ 585 uint64_t nBBs = 0; 586 587 /* Iterate sancov dir for files generated against target pid */ 588 snprintf(covFile, sizeof(covFile), "%s/%s", run->global->io.workDir, _HF_SANCOV_DIR); 589 pSanCovDir = opendir(covFile); 590 if (pSanCovDir == NULL) { 591 PLOG_E("opendir('%s')", covFile); 592 return false; 593 } 594 defer { closedir(pSanCovDir); }; 595 596 struct dirent* pDir = NULL; 597 while ((pDir = readdir(pSanCovDir)) != NULL) { 598 /* Parse files with target's pid */ 599 if (strstr(pDir->d_name, pidFSuffix)) { 600 snprintf(covFile, sizeof(covFile), "%s/%s/%s", run->global->io.workDir, _HF_SANCOV_DIR, 601 pDir->d_name); 602 dataBuf = files_mapFile(covFile, &dataFileSz, &dataFd, false); 603 if (dataBuf == NULL) { 604 LOG_E("Couldn't open and map '%s' in R/O mode", covFile); 605 return false; 606 } 607 defer { 608 munmap(dataBuf, dataFileSz); 609 close(dataFd); 610 }; 611 612 if (dataFileSz < 8) { 613 LOG_E("Coverage data file too short"); 614 return false; 615 } 616 617 /* Check magic values & derive PC length */ 618 uint64_t magic = util_getUINT64(dataBuf); 619 if (magic == kMagic32) { 620 is32bit = true; 621 } else if (magic == kMagic64) { 622 is32bit = false; 623 } else { 624 LOG_E("Invalid coverage data file"); 625 return false; 626 } 627 pos += 8; 628 629 /* 630 * Avoid cost of size checks inside raw data read loop by defining the read function 631 * & pivot size based on PC length. 632 */ 633 uint64_t (*pReadRawBBAddrFunc)(const uint8_t*) = NULL; 634 uint8_t pivot = 0; 635 if (is32bit) { 636 pReadRawBBAddrFunc = &util_getUINT32; 637 pivot = 4; 638 } else { 639 pReadRawBBAddrFunc = &util_getUINT64; 640 pivot = 8; 641 } 642 643 while (pos < dataFileSz) { 644 uint32_t bbAddr = pReadRawBBAddrFunc(dataBuf + pos); 645 pos += pivot; 646 if (bbAddr == 0x0) { 647 continue; 648 } 649 nBBs++; 650 } 651 } 652 } 653 654 /* Successful parsing - update fuzzer worker counters */ 655 run->sanCovCnts.hitBBCnt = nBBs; 656 657 if (run->global->linux.pid == 0 && run->global->persistent == false) { 658 unlink(covFile); 659 } 660 return true; 661 } 662 663 /* 664 * Sanitizer coverage data are stored in FS can be parsed via two methods: 665 * raw unpack & separate bin/DSO sancov file. Separate bin/DSO sancov file 666 * method is usually avoided since coverage data are lost if sanitizer unhandled 667 * signal. Additionally, the FS I/O overhead is bigger compared to raw unpack 668 * method which uses runtime data structures. 669 * 670 * Enabled methods are controlled from sanitizer flags in arch.c 671 */ 672 void sancov_Analyze(run_t* run) { 673 if (!run->global->useSanCov) { 674 return; 675 } 676 /* 677 * For now supported methods are implemented in fail-over nature. This will 678 * change in the future when best method is concluded. 679 */ 680 if (sancov_sanCovParseRaw(run) == false) { 681 sancov_sanCovParse(run); 682 } 683 } 684 685 bool sancov_Init(honggfuzz_t* hfuzz) { 686 if (hfuzz->useSanCov == false) { 687 return true; 688 } 689 sancov_trieCreate(&hfuzz->covMetadata); 690 691 char sanCovOutDir[PATH_MAX] = {0}; 692 snprintf(sanCovOutDir, sizeof(sanCovOutDir), "%s/%s", hfuzz->io.workDir, _HF_SANCOV_DIR); 693 if (!files_exists(sanCovOutDir)) { 694 if (mkdir(sanCovOutDir, S_IRWXU | S_IXGRP | S_IXOTH) != 0) { 695 PLOG_E("mkdir() '%s' failed", sanCovOutDir); 696 } 697 } 698 699 return true; 700 } 701