Home | History | Annotate | Download | only in hfuzz_cc
      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