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