1 #include <errno.h> 2 #include <fcntl.h> 3 #include <inttypes.h> 4 #include <libgen.h> 5 #include <limits.h> 6 #include <stdbool.h> 7 #include <stddef.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <unistd.h> 14 15 #include "honggfuzz.h" 16 #include "libhfcommon/common.h" 17 #include "libhfcommon/files.h" 18 #include "libhfcommon/log.h" 19 #include "libhfcommon/util.h" 20 21 #define ARGS_MAX 4096 22 23 static bool isCXX = false; 24 static bool isGCC = false; 25 26 /* Embed libhfuzz.a inside this binary */ 27 __asm__("\n" 28 " .global lhfuzz_start\n" 29 " .global lhfuzz_end\n" 30 "lhfuzz_start:\n" 31 " .incbin \"libhfuzz/libhfuzz.a\"\n" 32 "lhfuzz_end:\n" 33 "\n" 34 " .global lhfnetdriver_start\n" 35 " .global lhfnetdriver_end\n" 36 "lhfnetdriver_start:\n" 37 " .incbin \"libhfnetdriver/libhfnetdriver.a\"\n" 38 "lhfnetdriver_end:\n" 39 "\n"); 40 41 static const char* _basename(const char* path) { 42 static __thread char fname[PATH_MAX]; 43 /* basename() can modify the argument (sic!) */ 44 snprintf(fname, sizeof(fname), "%s", path); 45 return basename(fname); 46 } 47 48 static bool useASAN() { 49 if (getenv("HFUZZ_CC_ASAN")) { 50 return true; 51 } 52 return false; 53 } 54 55 static bool useMSAN() { 56 if (getenv("HFUZZ_CC_MSAN")) { 57 return true; 58 } 59 return false; 60 } 61 62 static bool useUBSAN() { 63 if (getenv("HFUZZ_CC_UBSAN")) { 64 return true; 65 } 66 return false; 67 } 68 69 static bool useM32() { 70 if (getenv("HFUZZ_FORCE_M32")) { 71 return true; 72 } 73 return false; 74 } 75 76 static bool useGccGE8() { 77 if (getenv("HFUZZ_CC_USE_GCC_GE_8")) { 78 return true; 79 } 80 return false; 81 } 82 83 static bool isLDMode(int argc, char** argv) { 84 for (int i = 1; i < argc; i++) { 85 if (strcmp(argv[i], "--version") == 0) { 86 return false; 87 } 88 if (strcmp(argv[i], "-c") == 0) { 89 return false; 90 } 91 if (strcmp(argv[i], "-E") == 0) { 92 return false; 93 } 94 if (strcmp(argv[i], "-S") == 0) { 95 return false; 96 } 97 if (strcmp(argv[i], "-shared") == 0) { 98 return false; 99 } 100 } 101 return true; 102 } 103 104 static bool isFSanitizeFuzzer(int argc, char** argv) { 105 for (int i = 1; i < argc; i++) { 106 if (strcmp(argv[i], "-fsanitize=fuzzer") == 0) { 107 return true; 108 } 109 } 110 return false; 111 } 112 113 static int hf_execvp(const char* file, char** argv) { 114 argv[0] = (char*)file; 115 return execvp(file, argv); 116 } 117 118 static int execCC(int argc, char** argv) { 119 if (useASAN()) { 120 argv[argc++] = "-fsanitize=address"; 121 } 122 if (useMSAN()) { 123 argv[argc++] = "-fsanitize=memory"; 124 } 125 if (useUBSAN()) { 126 argv[argc++] = "-fsanitize=undefined"; 127 } 128 argv[argc] = NULL; 129 130 if (isCXX) { 131 const char* cxx_path = getenv("HFUZZ_CXX_PATH"); 132 if (cxx_path != NULL) { 133 hf_execvp(cxx_path, argv); 134 PLOG_E("execvp('%s')", cxx_path); 135 return EXIT_FAILURE; 136 } 137 } else { 138 const char* cc_path = getenv("HFUZZ_CC_PATH"); 139 if (cc_path != NULL) { 140 hf_execvp(cc_path, argv); 141 PLOG_E("execvp('%s')", cc_path); 142 return EXIT_FAILURE; 143 } 144 } 145 146 if (isGCC) { 147 if (isCXX) { 148 hf_execvp("g++", argv); 149 hf_execvp("gcc", argv); 150 } else { 151 hf_execvp("gcc", argv); 152 } 153 } else { 154 if (isCXX) { 155 /* Try the default one, then newest ones (hopefully) first */ 156 hf_execvp("clang++", argv); 157 hf_execvp("clang++-devel", argv); 158 hf_execvp("clang++-10.0", argv); 159 hf_execvp("clang++-10", argv); 160 hf_execvp("clang++-9.0", argv); 161 hf_execvp("clang++-9", argv); 162 hf_execvp("clang++-8.0", argv); 163 hf_execvp("clang++-8", argv); 164 hf_execvp("clang++-7.0", argv); 165 hf_execvp("clang++-7", argv); 166 hf_execvp("clang++-6.0", argv); 167 hf_execvp("clang++-6", argv); 168 hf_execvp("clang++-5.0", argv); 169 hf_execvp("clang++-5", argv); 170 hf_execvp("clang", argv); 171 } else { 172 /* Try the default one, then newest ones (hopefully) first */ 173 hf_execvp("clang", argv); 174 hf_execvp("clang-devel", argv); 175 hf_execvp("clang-10.0", argv); 176 hf_execvp("clang-10", argv); 177 hf_execvp("clang-9.0", argv); 178 hf_execvp("clang-9", argv); 179 hf_execvp("clang-8.0", argv); 180 hf_execvp("clang-8", argv); 181 hf_execvp("clang-7.0", argv); 182 hf_execvp("clang-7", argv); 183 hf_execvp("clang-6.0", argv); 184 hf_execvp("clang-6", argv); 185 hf_execvp("clang-5.0", argv); 186 hf_execvp("clang-5", argv); 187 } 188 } 189 190 PLOG_F("execvp('%s')", argv[0]); 191 return EXIT_FAILURE; 192 } 193 194 /* It'll point back to the libhfuzz's source tree */ 195 char* getIncPaths(void) { 196 #if !defined(_HFUZZ_INC_PATH) 197 #error \ 198 "You need to define _HFUZZ_INC_PATH to a directory with the directory called 'includes', containing honggfuzz's lib* includes. Typically it'd be the build/sources dir" 199 #endif 200 201 static char path[PATH_MAX]; 202 snprintf(path, sizeof(path), "-I%s/includes/", HF_XSTR(_HFUZZ_INC_PATH)); 203 return path; 204 } 205 206 static bool getLibPath( 207 const char* name, const char* env, const uint8_t* start, const uint8_t* end, char* path) { 208 const char* libEnvLoc = getenv(env); 209 if (libEnvLoc) { 210 snprintf(path, PATH_MAX, "%s", libEnvLoc); 211 return true; 212 } 213 214 ptrdiff_t len = (uintptr_t)end - (uintptr_t)start; 215 uint64_t crc64 = util_CRC64(start, len); 216 snprintf(path, PATH_MAX, "/tmp/%s.%d.%" PRIx64 ".a", name, geteuid(), crc64); 217 218 /* Does the library exist, belongs to the user, and is of expected size? */ 219 struct stat st; 220 if (stat(path, &st) != -1 && st.st_size == len && st.st_uid == geteuid()) { 221 return true; 222 } 223 224 /* If not, create it with atomic rename() */ 225 char template[] = "/tmp/lib.honggfuzz.a.XXXXXX"; 226 int fd = TEMP_FAILURE_RETRY(mkostemp(template, O_CLOEXEC)); 227 if (fd == -1) { 228 PLOG_E("mkostemp('%s')", template); 229 return false; 230 } 231 defer { 232 close(fd); 233 }; 234 235 if (!files_writeToFd(fd, start, len)) { 236 PLOG_E("Couldn't write to '%s'", template); 237 unlink(template); 238 return false; 239 } 240 241 if (TEMP_FAILURE_RETRY(rename(template, path)) == -1) { 242 PLOG_E("Couldn't rename('%s', '%s')", template, path); 243 unlink(template); 244 return false; 245 } 246 247 return true; 248 } 249 250 static char* getLibHfuzzPath() { 251 extern uint8_t lhfuzz_start __asm__("lhfuzz_start"); 252 extern uint8_t lhfuzz_end __asm__("lhfuzz_end"); 253 254 static char path[PATH_MAX] = {}; 255 if (path[0]) { 256 return path; 257 } 258 if (!getLibPath("libhfuzz", "HFUZZ_LHFUZZ_PATH", &lhfuzz_start, &lhfuzz_end, path)) { 259 LOG_F("Couldn't create the temporary libhfuzz.a"); 260 } 261 return path; 262 } 263 264 static char* getLibHFNetDriverPath() { 265 extern uint8_t lhfnetdriver_start __asm__("lhfnetdriver_start"); 266 extern uint8_t lhfnetdriver_end __asm__("lhfnetdriver_end"); 267 268 static char path[PATH_MAX] = {}; 269 if (path[0]) { 270 return path; 271 } 272 if (!getLibPath("libhfnetdriver", "HFUZZ_LHFNETDRIVER_PATH", &lhfnetdriver_start, 273 &lhfnetdriver_end, path)) { 274 LOG_F("Couldn't create the temporary libhfnetdriver.a"); 275 } 276 return path; 277 } 278 279 static void commonOpts(int* j, char** args) { 280 args[(*j)++] = getIncPaths(); 281 if (isGCC) { 282 if (useGccGE8()) { 283 /* gcc-8 offers trace-cmp as well, but it's not that widely used yet */ 284 args[(*j)++] = "-fsanitize-coverage=trace-pc,trace-cmp"; 285 } else { 286 /* trace-pc is the best that gcc-6/7 currently offers */ 287 args[(*j)++] = "-fsanitize-coverage=trace-pc"; 288 } 289 } else { 290 args[(*j)++] = "-Wno-unused-command-line-argument"; 291 args[(*j)++] = "-fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls"; 292 args[(*j)++] = "-mllvm"; 293 args[(*j)++] = "-sanitizer-coverage-prune-blocks=0"; 294 args[(*j)++] = "-mllvm"; 295 args[(*j)++] = "-sanitizer-coverage-level=3"; 296 } 297 298 /* 299 * Make the execution flow more explicit, allowing for more code blocks 300 * (and better code coverage estimates) 301 */ 302 args[(*j)++] = "-fno-inline"; 303 args[(*j)++] = "-fno-builtin"; 304 args[(*j)++] = "-fno-omit-frame-pointer"; 305 args[(*j)++] = "-D__NO_STRING_INLINES"; 306 307 /* Make it possible to use the libhfnetdriver */ 308 args[(*j)++] = "-DHFND_FUZZING_ENTRY_FUNCTION_CXX(x,y)=" 309 "extern \"C\" int HonggfuzzNetDriver_main(x,y);" 310 "extern const char* LIBHFNETDRIVER_module_netdriver;" 311 "const char** LIBHFNETDRIVER_module_main = &LIBHFNETDRIVER_module_netdriver;" 312 "int HonggfuzzNetDriver_main(x,y)"; 313 args[(*j)++] = "-DHFND_FUZZING_ENTRY_FUNCTION(x,y)=" 314 "int HonggfuzzNetDriver_main(x,y);" 315 "extern const char* LIBHFNETDRIVER_module_netdriver;" 316 "const char** LIBHFNETDRIVER_module_main = &LIBHFNETDRIVER_module_netdriver;" 317 "int HonggfuzzNetDriver_main(x,y)"; 318 319 if (useM32()) { 320 args[(*j)++] = "-m32"; 321 } 322 } 323 324 static int ccMode(int argc, char** argv) { 325 char* args[ARGS_MAX]; 326 327 int j = 0; 328 if (isCXX) { 329 args[j++] = "c++"; 330 } else { 331 args[j++] = "cc"; 332 } 333 334 commonOpts(&j, args); 335 336 for (int i = 1; i < argc; i++) { 337 args[j++] = argv[i]; 338 } 339 340 return execCC(j, args); 341 } 342 343 static int ldMode(int argc, char** argv) { 344 char* args[ARGS_MAX]; 345 346 int j = 0; 347 if (isCXX) { 348 args[j++] = "c++"; 349 } else { 350 args[j++] = "cc"; 351 } 352 353 commonOpts(&j, args); 354 355 /* MacOS X linker doesn't like those */ 356 #ifndef _HF_ARCH_DARWIN 357 /* Intercept common *cmp functions */ 358 args[j++] = "-Wl,--wrap=strcmp"; 359 args[j++] = "-Wl,--wrap=strcasecmp"; 360 args[j++] = "-Wl,--wrap=strncmp"; 361 args[j++] = "-Wl,--wrap=strncasecmp"; 362 args[j++] = "-Wl,--wrap=strstr"; 363 args[j++] = "-Wl,--wrap=strcasestr"; 364 args[j++] = "-Wl,--wrap=memcmp"; 365 args[j++] = "-Wl,--wrap=bcmp"; 366 args[j++] = "-Wl,--wrap=memmem"; 367 args[j++] = "-Wl,--wrap=strcpy"; 368 /* Apache's httpd mem/str cmp functions */ 369 args[j++] = "-Wl,--wrap=ap_cstr_casecmp"; 370 args[j++] = "-Wl,--wrap=ap_cstr_casecmpn"; 371 args[j++] = "-Wl,--wrap=ap_strcasestr"; 372 args[j++] = "-Wl,--wrap=apr_cstr_casecmp"; 373 args[j++] = "-Wl,--wrap=apr_cstr_casecmpn"; 374 /* Frequently used time-constant *SSL functions */ 375 args[j++] = "-Wl,--wrap=CRYPTO_memcmp"; 376 args[j++] = "-Wl,--wrap=OPENSSL_memcmp"; 377 args[j++] = "-Wl,--wrap=OPENSSL_strcasecmp"; 378 args[j++] = "-Wl,--wrap=OPENSSL_strncasecmp"; 379 args[j++] = "-Wl,--wrap=memcmpct"; 380 /* Frequently used libXML2 functions */ 381 args[j++] = "-Wl,--wrap=xmlStrncmp"; 382 args[j++] = "-Wl,--wrap=xmlStrcmp"; 383 args[j++] = "-Wl,--wrap=xmlStrEqual"; 384 args[j++] = "-Wl,--wrap=xmlStrcasecmp"; 385 args[j++] = "-Wl,--wrap=xmlStrncasecmp"; 386 args[j++] = "-Wl,--wrap=xmlStrstr"; 387 args[j++] = "-Wl,--wrap=xmlStrcasestr"; 388 /* Some Samba functions */ 389 args[j++] = "-Wl,--wrap=memcmp_const_time"; 390 args[j++] = "-Wl,--wrap=strcsequal"; 391 #endif /* _HF_ARCH_DARWIN */ 392 393 for (int i = 1; i < argc; i++) { 394 args[j++] = argv[i]; 395 } 396 397 /* Reference standard honggfuzz libraries (libhfuzz and libhfnetdriver) */ 398 args[j++] = getLibHFNetDriverPath(); 399 args[j++] = getLibHfuzzPath(); 400 args[j++] = getLibHFNetDriverPath(); 401 402 /* Pull modules defining the following symbols (if they exist) */ 403 #ifdef _HF_ARCH_DARWIN 404 args[j++] = "-Wl,-U,_LIBHFNETDRIVER_module_main", 405 args[j++] = "-Wl,-U,_LIBHFUZZ_module_instrument"; 406 args[j++] = "-Wl,-U,_LIBHFUZZ_module_memorycmp"; 407 #else /* _HF_ARCH_DARWIN */ 408 args[j++] = "-Wl,-u,LIBHFNETDRIVER_module_main", 409 args[j++] = "-Wl,-u,LIBHFUZZ_module_instrument"; 410 args[j++] = "-Wl,-u,LIBHFUZZ_module_memorycmp"; 411 #endif /* _HF_ARCH_DARWIN */ 412 413 /* Needed by the libhfcommon */ 414 args[j++] = "-pthread"; 415 416 /* Disable -fsanitize=fuzzer */ 417 if (isFSanitizeFuzzer(argc, argv)) { 418 args[j++] = "-fno-sanitize=fuzzer"; 419 } 420 421 return execCC(j, args); 422 } 423 424 static bool baseNameContains(const char* path, const char* str) { 425 if (strstr(_basename(path), str)) { 426 return true; 427 } 428 return false; 429 } 430 431 int main(int argc, char** argv) { 432 if (baseNameContains(argv[0], "++")) { 433 isCXX = true; 434 } 435 if (baseNameContains(argv[0], "-gcc")) { 436 isGCC = true; 437 } 438 if (baseNameContains(argv[0], "-g++")) { 439 isGCC = true; 440 } 441 if (argc <= 1) { 442 return execCC(argc, argv); 443 } 444 if (argc > (ARGS_MAX - 128)) { 445 LOG_F("'%s': Too many positional arguments: %d", argv[0], argc); 446 return EXIT_FAILURE; 447 } 448 449 if (isLDMode(argc, argv)) { 450 return ldMode(argc, argv); 451 } 452 return ccMode(argc, argv); 453 } 454