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  */
sancov_newBitmap(uint32_t capacity)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 
sancov_queryBitmap(bitmap_t * pBM,uint32_t index)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 
sancov_setBitmap(bitmap_t * pBM,uint32_t index)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 
sancov_destroyBitmap(bitmap_t * pBM)117 static inline void sancov_destroyBitmap(bitmap_t* pBM) {
118     free(pBM->pChunks);
119     free(pBM);
120 }
121 
122 /*
123  * Trie implementation
124  */
sancov_trieCreateNode(char key)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 
sancov_trieSearch(node_t * root,const char * key)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 
sancov_trieAdd(node_t ** root,const char * key)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 
sancov_trieFreeNode(node_t * node)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 
sancov_trieCreate(node_t ** root)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 */
sancov_trieDestroy(node_t * root)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 */
sancov_interpSearch(uint64_t * buf,uint64_t size,uint64_t key)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) */
sancov_qsortCmp(const void * a,const void * b)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 
sancov_sanCovParseRaw(run_t * run)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 
sancov_sanCovParse(run_t * run)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  */
sancov_Analyze(run_t * run)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 
sancov_Init(honggfuzz_t * hfuzz)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