Home | History | Annotate | Download | only in libhfuzz
      1 #include "instrument.h"
      2 
      3 #include <ctype.h>
      4 #include <errno.h>
      5 #include <fcntl.h>
      6 #include <inttypes.h>
      7 #include <stdbool.h>
      8 #include <stdint.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <sys/mman.h>
     13 #include <sys/stat.h>
     14 #include <sys/types.h>
     15 #include <unistd.h>
     16 
     17 #include "honggfuzz.h"
     18 #include "libhfcommon/common.h"
     19 #include "libhfcommon/log.h"
     20 #include "libhfcommon/util.h"
     21 
     22 __attribute__((visibility("default"))) __attribute__((used))
     23 const char* const LIBHFUZZ_module_instrument = "LIBHFUZZ_module_instrument";
     24 
     25 /*
     26  * We require SSE4.2 with x86-(32|64) for the 'popcnt', as it's much faster than the software
     27  * emulation of gcc/clang
     28  */
     29 #if defined(__x86_64__) || defined(__i386__)
     30 #define ATTRIBUTE_X86_REQUIRE_SSE42 __attribute__((__target__("sse4.2")))
     31 #else
     32 #define ATTRIBUTE_X86_REQUIRE_SSE42
     33 #endif /* defined(__x86_64__) || defined(__i386__) */
     34 
     35 /*
     36  * If there's no _HF_BITMAP_FD available (running without the honggfuzz
     37  * supervisor), use a dummy bitmap and control structure located in the BSS
     38  */
     39 static feedback_t bbMapFb;
     40 feedback_t* feedback = &bbMapFb;
     41 uint32_t my_thread_no = 0;
     42 
     43 __attribute__((constructor)) static void initializeInstrument(void) {
     44     if (fcntl(_HF_LOG_FD, F_GETFD) != -1) {
     45         enum llevel_t ll = INFO;
     46         const char* llstr = getenv(_HF_LOG_LEVEL_ENV);
     47         if (llstr) {
     48             ll = atoi(llstr);
     49         }
     50         logInitLogFile(NULL, _HF_LOG_FD, ll);
     51     }
     52 
     53     char* my_thread_no_str = getenv(_HF_THREAD_NO_ENV);
     54     if (my_thread_no_str == NULL) {
     55         LOG_D("The '%s' envvar is not set", _HF_THREAD_NO_ENV);
     56         return;
     57     }
     58     my_thread_no = atoi(my_thread_no_str);
     59 
     60     if (my_thread_no >= _HF_THREAD_MAX) {
     61         LOG_F("Received (via envvar) my_thread_no > _HF_THREAD_MAX (%" PRIu32 " > %d)\n",
     62             my_thread_no, _HF_THREAD_MAX);
     63     }
     64 
     65     struct stat st;
     66     if (fstat(_HF_BITMAP_FD, &st) == -1) {
     67         return;
     68     }
     69     if (st.st_size != sizeof(feedback_t)) {
     70         LOG_F(
     71             "size of the feedback structure mismatch: st.size != sizeof(feedback_t) (%zu != %zu). "
     72             "Link your fuzzed binaries with the newest honggfuzz sources via hfuzz-clang(++)",
     73             (size_t)st.st_size, sizeof(feedback_t));
     74     }
     75     if ((feedback = mmap(NULL, sizeof(feedback_t), PROT_READ | PROT_WRITE, MAP_SHARED,
     76              _HF_BITMAP_FD, 0)) == MAP_FAILED) {
     77         PLOG_F("mmap(fd=%d, size=%zu) of the feedback structure failed", _HF_BITMAP_FD,
     78             sizeof(feedback_t));
     79     }
     80 
     81     /* Reset coverage counters to their initial state */
     82     instrumentClearNewCov();
     83 }
     84 
     85 /* Reset the counters of newly discovered edges/pcs/features */
     86 void instrumentClearNewCov() {
     87     feedback->pidFeedbackPc[my_thread_no] = 0U;
     88     feedback->pidFeedbackEdge[my_thread_no] = 0U;
     89     feedback->pidFeedbackCmp[my_thread_no] = 0U;
     90 }
     91 
     92 /*
     93  * -finstrument-functions
     94  */
     95 ATTRIBUTE_X86_REQUIRE_SSE42 void __cyg_profile_func_enter(void* func, void* caller) {
     96     register size_t pos =
     97         (((uintptr_t)func << 12) | ((uintptr_t)caller & 0xFFF)) & _HF_PERF_BITMAP_BITSZ_MASK;
     98     register uint8_t prev = ATOMIC_BTS(feedback->bbMapPc, pos);
     99     if (!prev) {
    100         ATOMIC_PRE_INC_RELAXED(feedback->pidFeedbackPc[my_thread_no]);
    101     }
    102 }
    103 
    104 ATTRIBUTE_X86_REQUIRE_SSE42 void __cyg_profile_func_exit(
    105     void* func HF_ATTR_UNUSED, void* caller HF_ATTR_UNUSED) {
    106     return;
    107 }
    108 
    109 /*
    110  * -fsanitize-coverage=trace-pc
    111  */
    112 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_pc(void) {
    113     register uintptr_t ret = (uintptr_t)__builtin_return_address(0) & _HF_PERF_BITMAP_BITSZ_MASK;
    114     register uint8_t prev = ATOMIC_BTS(feedback->bbMapPc, ret);
    115     if (!prev) {
    116         ATOMIC_PRE_INC_RELAXED(feedback->pidFeedbackPc[my_thread_no]);
    117     }
    118 }
    119 
    120 /*
    121  * -fsanitize-coverage=trace-cmp
    122  */
    123 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
    124     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    125     register uint8_t v = ((sizeof(Arg1) * 8) - __builtin_popcount(Arg1 ^ Arg2));
    126     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    127     if (prev < v) {
    128         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    129         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    130     }
    131 }
    132 
    133 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
    134     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    135     register uint8_t v = ((sizeof(Arg1) * 8) - __builtin_popcount(Arg1 ^ Arg2));
    136     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    137     if (prev < v) {
    138         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    139         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    140     }
    141 }
    142 
    143 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
    144     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    145     register uint8_t v = ((sizeof(Arg1) * 8) - __builtin_popcount(Arg1 ^ Arg2));
    146     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    147     if (prev < v) {
    148         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    149         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    150     }
    151 }
    152 
    153 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
    154     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    155     register uint8_t v = ((sizeof(Arg1) * 8) - __builtin_popcountll(Arg1 ^ Arg2));
    156     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    157     if (prev < v) {
    158         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    159         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    160     }
    161 }
    162 
    163 /*
    164  * Const versions of trace_cmp, we don't use any special handling for these
    165  *
    166  * For MacOS, these're weak aliases, as Darwin supports only them
    167  */
    168 
    169 #if defined(_HF_ARCH_DARWIN)
    170 #pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1
    171 #pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2
    172 #pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4
    173 #pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8
    174 #else
    175 void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2)
    176     __attribute__((alias("__sanitizer_cov_trace_cmp1")));
    177 void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2)
    178     __attribute__((alias("__sanitizer_cov_trace_cmp2")));
    179 void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2)
    180     __attribute__((alias("__sanitizer_cov_trace_cmp4")));
    181 void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2)
    182     __attribute__((alias("__sanitizer_cov_trace_cmp8")));
    183 #endif /* defined(_HF_ARCH_DARWIN) */
    184 
    185 /*
    186  * Cases[0] is number of comparison entries
    187  * Cases[1] is length of Val in bits
    188  */
    189 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t* Cases) {
    190     for (uint64_t i = 0; i < Cases[0]; i++) {
    191         uintptr_t pos = ((uintptr_t)__builtin_return_address(0) + i) % _HF_PERF_BITMAP_SIZE_16M;
    192         uint8_t v = (uint8_t)Cases[1] - __builtin_popcountll(Val ^ Cases[i + 2]);
    193         uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    194         if (prev < v) {
    195             ATOMIC_SET(feedback->bbMapCmp[pos], v);
    196             ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    197         }
    198     }
    199 }
    200 
    201 /*
    202  * Old version of __sanitizer_cov_trace_cmp[n]. Remove it at some point
    203  */
    204 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmp(
    205     uint64_t SizeAndType, uint64_t Arg1, uint64_t Arg2) {
    206     uint64_t CmpSize = (SizeAndType >> 32) / 8;
    207     switch (CmpSize) {
    208         case (sizeof(uint8_t)):
    209             __sanitizer_cov_trace_cmp1(Arg1, Arg2);
    210             return;
    211         case (sizeof(uint16_t)):
    212             __sanitizer_cov_trace_cmp2(Arg1, Arg2);
    213             return;
    214         case (sizeof(uint32_t)):
    215             __sanitizer_cov_trace_cmp4(Arg1, Arg2);
    216             return;
    217         case (sizeof(uint64_t)):
    218             __sanitizer_cov_trace_cmp8(Arg1, Arg2);
    219             return;
    220     }
    221 }
    222 
    223 /*
    224  * gcc-8 -fsanitize-coverage=trace-cmp trace hooks
    225  * TODO: evaluate, whether it makes sense to implement them
    226  */
    227 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmpf(
    228     float Arg1 HF_ATTR_UNUSED, float Arg2 HF_ATTR_UNUSED) {
    229 }
    230 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_cmpd(
    231     double Arg1 HF_ATTR_UNUSED, double Arg2 HF_ATTR_UNUSED) {
    232 }
    233 
    234 /*
    235  * -fsanitize-coverage=trace-div
    236  */
    237 void __sanitizer_cov_trace_div8(uint64_t Val) {
    238     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    239     uint8_t v = ((sizeof(Val) * 8) - __builtin_popcountll(Val));
    240     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    241     if (prev < v) {
    242         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    243         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    244     }
    245 }
    246 
    247 void __sanitizer_cov_trace_div4(uint32_t Val) {
    248     uintptr_t pos = (uintptr_t)__builtin_return_address(0) % _HF_PERF_BITMAP_SIZE_16M;
    249     uint8_t v = ((sizeof(Val) * 8) - __builtin_popcount(Val));
    250     uint8_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    251     if (prev < v) {
    252         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    253         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    254     }
    255 }
    256 
    257 /*
    258  * -fsanitize-coverage=indirect-calls
    259  */
    260 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_pc_indir(uintptr_t callee) {
    261     register size_t pos1 = (uintptr_t)__builtin_return_address(0) << 12;
    262     register size_t pos2 = callee & 0xFFF;
    263     register size_t pos = (pos1 | pos2) & _HF_PERF_BITMAP_BITSZ_MASK;
    264 
    265     register uint8_t prev = ATOMIC_BTS(feedback->bbMapPc, pos);
    266     if (!prev) {
    267         ATOMIC_PRE_INC_RELAXED(feedback->pidFeedbackPc[my_thread_no]);
    268     }
    269 }
    270 
    271 /*
    272  * In LLVM-4.0 it's marked (probably mistakenly) as non-weak symbol, so we need to mark it as weak
    273  * here
    274  */
    275 __attribute__((weak)) ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_indir_call16(
    276     void* callee, void* callee_cache16[] HF_ATTR_UNUSED) {
    277     register size_t pos1 = (uintptr_t)__builtin_return_address(0) << 12;
    278     register size_t pos2 = (uintptr_t)callee & 0xFFF;
    279     register size_t pos = (pos1 | pos2) & _HF_PERF_BITMAP_BITSZ_MASK;
    280 
    281     register uint8_t prev = ATOMIC_BTS(feedback->bbMapPc, pos);
    282     if (!prev) {
    283         ATOMIC_PRE_INC_RELAXED(feedback->pidFeedbackPc[my_thread_no]);
    284     }
    285 }
    286 
    287 /*
    288  * -fsanitize-coverage=trace-pc-guard
    289  */
    290 static bool guards_initialized = false;
    291 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_pc_guard_init(
    292     uint32_t* start, uint32_t* stop) {
    293     guards_initialized = true;
    294     static uint32_t n = 1U;
    295     for (uint32_t* x = start; x < stop; x++, n++) {
    296         if (n >= _HF_PC_GUARD_MAX) {
    297             LOG_F("This process has too many PC guards:%" PRIu32
    298                   " (current module:%tu start:%p stop:%p)\n",
    299                 n, ((uintptr_t)stop - (uintptr_t)start) / sizeof(start), start, stop);
    300         }
    301         /* If the corresponding PC was already hit, map this specific guard as non-interesting (0)
    302          */
    303         *x = ATOMIC_GET(feedback->pcGuardMap[n]) ? 0U : n;
    304     }
    305 }
    306 
    307 ATTRIBUTE_X86_REQUIRE_SSE42 void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
    308 #if defined(__ANDROID__)
    309     // ANDROID: Bionic invokes routines that Honggfuzz wraps, before either
    310     //          *SAN or Honggfuzz have initialized.  Check to see if Honggfuzz
    311     //          has initialized -- if not, force *SAN to initialize (otherwise
    312     //          _strcmp() will crash, as it is *SAN-instrumented).
    313     //
    314     //          Defer all trace_pc_guard activity until trace_pc_guard_init is
    315     //          invoked via sancov.module_ctor in the normal process of things.
    316     if (!guards_initialized) {
    317         void __asan_init(void) __attribute__((weak));
    318         if (__asan_init) {
    319             __asan_init();
    320         }
    321         void __msan_init(void) __attribute__((weak));
    322         if (__msan_init) {
    323             __msan_init();
    324         }
    325         void __ubsan_init(void) __attribute__((weak));
    326         if (__ubsan_init) {
    327             __ubsan_init();
    328         }
    329         void __tsan_init(void) __attribute__((weak));
    330         if (__tsan_init) {
    331             __tsan_init();
    332         }
    333         return;
    334     }
    335 #endif /* defined(__ANDROID__) */
    336     if (*guard == 0U) {
    337         return;
    338     }
    339     bool prev = ATOMIC_XCHG(feedback->pcGuardMap[*guard], true);
    340     if (prev == false) {
    341         ATOMIC_PRE_INC_RELAXED(feedback->pidFeedbackEdge[my_thread_no]);
    342     }
    343     *guard = 0U;
    344 }
    345 
    346 void instrumentUpdateCmpMap(uintptr_t addr, uint32_t v) {
    347     uintptr_t pos = addr % _HF_PERF_BITMAP_SIZE_16M;
    348     uint32_t prev = ATOMIC_GET(feedback->bbMapCmp[pos]);
    349     if (prev < v) {
    350         ATOMIC_SET(feedback->bbMapCmp[pos], v);
    351         ATOMIC_POST_ADD(feedback->pidFeedbackCmp[my_thread_no], v - prev);
    352     }
    353 }
    354