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