Home | History | Annotate | Download | only in honggfuzz
      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