• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
_basename(const char * path)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 
useASAN()48 static bool useASAN() {
49     if (getenv("HFUZZ_CC_ASAN")) {
50         return true;
51     }
52     return false;
53 }
54 
useMSAN()55 static bool useMSAN() {
56     if (getenv("HFUZZ_CC_MSAN")) {
57         return true;
58     }
59     return false;
60 }
61 
useUBSAN()62 static bool useUBSAN() {
63     if (getenv("HFUZZ_CC_UBSAN")) {
64         return true;
65     }
66     return false;
67 }
68 
useM32()69 static bool useM32() {
70     if (getenv("HFUZZ_FORCE_M32")) {
71         return true;
72     }
73     return false;
74 }
75 
useGccGE8()76 static bool useGccGE8() {
77     if (getenv("HFUZZ_CC_USE_GCC_GE_8")) {
78         return true;
79     }
80     return false;
81 }
82 
isLDMode(int argc,char ** argv)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 
isFSanitizeFuzzer(int argc,char ** argv)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 
hf_execvp(const char * file,char ** argv)113 static int hf_execvp(const char* file, char** argv) {
114     argv[0] = (char*)file;
115     return execvp(file, argv);
116 }
117 
execCC(int argc,char ** argv)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 */
getIncPaths(void)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 
getLibPath(const char * name,const char * env,const uint8_t * start,const uint8_t * end,char * path)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 
getLibHfuzzPath()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 
getLibHFNetDriverPath()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 
commonOpts(int * j,char ** args)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 
ccMode(int argc,char ** argv)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 
ldMode(int argc,char ** argv)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 
baseNameContains(const char * path,const char * str)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 
main(int argc,char ** argv)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