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