• 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 "libcommon/common.h"
17 #include "libcommon/files.h"
18 #include "libcommon/log.h"
19 #include "libcommon/util.h"
20 
21 #define ARGS_MAX 4096
22 #define __XSTR(x) #x
23 #define _XSTR(x) __XSTR(x)
24 
25 static char lhfuzzPath[PATH_MAX] = {0};
26 
27 static bool isCXX = false;
28 static bool isGCC = false;
29 
30 /* Embed libhfuzz.a inside this binary */
31 __asm__(
32     "\n"
33     "   .global lhfuzz_start\n"
34     "   .global lhfuzz_end\n"
35     "lhfuzz_start:\n"
36     "   .incbin \"libhfuzz/libhfuzz.a\"\n"
37     "lhfuzz_end:\n"
38     "\n");
39 
useASAN()40 static bool useASAN() {
41     if (getenv("HFUZZ_CC_ASAN") != NULL) {
42         return true;
43     }
44     return false;
45 }
46 
useMSAN()47 static bool useMSAN() {
48     if (getenv("HFUZZ_CC_MSAN") != NULL) {
49         return true;
50     }
51     return false;
52 }
53 
useUBSAN()54 static bool useUBSAN() {
55     if (getenv("HFUZZ_CC_UBSAN") != NULL) {
56         return true;
57     }
58     return false;
59 }
60 
isLDMode(int argc,char ** argv)61 static bool isLDMode(int argc, char** argv) {
62     for (int i = 1; i < argc; i++) {
63         if (strcmp(argv[i], "--version") == 0) {
64             return false;
65         }
66         if (strcmp(argv[i], "-c") == 0) {
67             return false;
68         }
69         if (strcmp(argv[i], "-E") == 0) {
70             return false;
71         }
72         if (strcmp(argv[i], "-S") == 0) {
73             return false;
74         }
75     }
76     return true;
77 }
78 
execCC(int argc,char ** argv)79 static int execCC(int argc, char** argv) {
80     if (useASAN()) {
81         argv[argc++] = "-fsanitize=address";
82     }
83     if (useMSAN()) {
84         argv[argc++] = "-fsanitize=memory";
85     }
86     if (useUBSAN()) {
87         argv[argc++] = "-fsanitize=undefined";
88     }
89     argv[argc] = NULL;
90 
91     if (isCXX) {
92         const char* cxx_path = getenv("HFUZZ_CXX_PATH");
93         if (cxx_path != NULL) {
94             execvp(cxx_path, argv);
95             PLOG_E("execvp('%s')", cxx_path);
96             return EXIT_FAILURE;
97         }
98     } else {
99         const char* cc_path = getenv("HFUZZ_CC_PATH");
100         if (cc_path != NULL) {
101             execvp(cc_path, argv);
102             PLOG_E("execvp('%s')", cc_path);
103             return EXIT_FAILURE;
104         }
105     }
106 
107     if (isGCC) {
108         if (isCXX) {
109             execvp("g++", argv);
110             execvp("gcc", argv);
111         } else {
112             execvp("gcc", argv);
113         }
114     } else {
115         if (isCXX) {
116             execvp("clang++-devel", argv);
117             execvp("clang++-7.0", argv);
118             execvp("clang++-6.0", argv);
119             execvp("clang++-5.0", argv);
120             execvp("clang++-4.0", argv);
121             execvp("clang++", argv);
122             execvp("clang", argv);
123         } else {
124             execvp("clang-devel", argv);
125             execvp("clang-7.0", argv);
126             execvp("clang-6.0", argv);
127             execvp("clang-5.0", argv);
128             execvp("clang-4.0", argv);
129             execvp("clang", argv);
130         }
131     }
132 
133     PLOG_E("execvp('%s')", argv[0]);
134     return EXIT_FAILURE;
135 }
136 
137 /* It'll point back to the libhfuzz's source tree */
getIncPaths(void)138 char* getIncPaths(void) {
139 #if !defined(_HFUZZ_INC_PATH)
140 #error "You need to define _HFUZZ_INC_PATH"
141 #endif
142 
143     static char path[PATH_MAX];
144     snprintf(path, sizeof(path), "-I%s/includes/", _XSTR(_HFUZZ_INC_PATH));
145     return path;
146 }
147 
commonOpts(int * j,char ** args)148 static void commonOpts(int* j, char** args) {
149     args[(*j)++] = getIncPaths();
150     if (isGCC) {
151         /* That's the best gcc-6/7 currently offers */
152         args[(*j)++] = "-fsanitize-coverage=trace-pc";
153     } else {
154         args[(*j)++] = "-Wno-unused-command-line-argument";
155         args[(*j)++] = "-fsanitize-coverage=trace-pc-guard,trace-cmp,indirect-calls";
156         args[(*j)++] = "-mllvm";
157         args[(*j)++] = "-sanitizer-coverage-prune-blocks=0";
158         args[(*j)++] = "-mllvm";
159         args[(*j)++] = "-sanitizer-coverage-level=3";
160     }
161 
162     /*
163      * Make the execution flow more explicit, allowing for more code blocks
164      * (and better code coverage estimates)
165      */
166     args[(*j)++] = "-fno-inline";
167     args[(*j)++] = "-fno-builtin";
168     args[(*j)++] = "-fno-omit-frame-pointer";
169     args[(*j)++] = "-D__NO_STRING_INLINES";
170 
171     if (getenv("HFUZZ_FORCE_M32")) {
172         args[(*j)++] = "-m32";
173     }
174 }
175 
getLibHfuzz(void)176 static bool getLibHfuzz(void) {
177     const char* lhfuzzEnvLoc = getenv("HFUZZ_LHFUZZ_PATH");
178     if (lhfuzzEnvLoc) {
179         snprintf(lhfuzzPath, sizeof(lhfuzzPath), "%s", lhfuzzEnvLoc);
180         return true;
181     }
182 
183     extern uint8_t lhfuzz_start __asm__("lhfuzz_start");
184     extern uint8_t lhfuzz_end __asm__("lhfuzz_end");
185     ptrdiff_t len = (uintptr_t)&lhfuzz_end - (uintptr_t)&lhfuzz_start;
186     if (lhfuzzPath[0] == 0) {
187         uint64_t crc64 = util_CRC64(&lhfuzz_start, len);
188         snprintf(
189             lhfuzzPath, sizeof(lhfuzzPath), "/tmp/libhfuzz.%d.%" PRIx64 ".a", geteuid(), crc64);
190     }
191 
192     /* Does the library exist, belongs to the user and is of the expected size */
193     struct stat st;
194     if (stat(lhfuzzPath, &st) != -1) {
195         if (st.st_size == len && st.st_uid == geteuid()) {
196             return true;
197         }
198     }
199 
200     /* If not, provide it with atomic rename() */
201     char template[] = "/tmp/libhfuzz.a.XXXXXX";
202     int fd = mkostemp(template, O_CLOEXEC);
203     if (fd == -1) {
204         PLOG_E("mkostemp('%s')", template);
205         return false;
206     }
207     defer { close(fd); };
208 
209     bool ret = files_writeToFd(fd, &lhfuzz_start, len);
210     if (!ret) {
211         PLOG_E("Couldn't write to '%s'", template);
212         return false;
213     }
214 
215     if (rename(template, lhfuzzPath) == -1) {
216         PLOG_E("Couldn't rename('%s', '%s')", template, lhfuzzPath);
217         unlink(template);
218         return false;
219     }
220 
221     return true;
222 }
223 
ccMode(int argc,char ** argv)224 static int ccMode(int argc, char** argv) {
225     char* args[ARGS_MAX];
226 
227     int j = 0;
228     if (isCXX) {
229         args[j++] = "c++";
230     } else {
231         args[j++] = "cc";
232     }
233     commonOpts(&j, args);
234 
235     for (int i = 1; i < argc; i++) {
236         args[j++] = argv[i];
237     }
238 
239     return execCC(j, args);
240 }
241 
ldMode(int argc,char ** argv)242 static int ldMode(int argc, char** argv) {
243     if (!getLibHfuzz()) {
244         return EXIT_FAILURE;
245     }
246 
247     char* args[ARGS_MAX];
248 
249     int j = 0;
250     if (isCXX) {
251         args[j++] = "c++";
252     } else {
253         args[j++] = "cc";
254     }
255 
256     /* Intercept common *cmp functions */
257     args[j++] = "-Wl,--wrap=strcmp";
258     args[j++] = "-Wl,--wrap=strcasecmp";
259     args[j++] = "-Wl,--wrap=strncmp";
260     args[j++] = "-Wl,--wrap=strncasecmp";
261     args[j++] = "-Wl,--wrap=strstr";
262     args[j++] = "-Wl,--wrap=strcasestr";
263     args[j++] = "-Wl,--wrap=memcmp";
264     args[j++] = "-Wl,--wrap=bcmp";
265     args[j++] = "-Wl,--wrap=memmem";
266     /* Apache's httpd mem/str cmp functions */
267     args[j++] = "-Wl,--wrap=ap_cstr_casecmp";
268     args[j++] = "-Wl,--wrap=ap_cstr_casecmpn";
269     args[j++] = "-Wl,--wrap=ap_strcasestr";
270     args[j++] = "-Wl,--wrap=apr_cstr_casecmp";
271     args[j++] = "-Wl,--wrap=apr_cstr_casecmpn";
272     /* Frequently used time-constant *SSL functions */
273     args[j++] = "-Wl,--wrap=CRYPTO_memcmp";
274     args[j++] = "-Wl,--wrap=OPENSSL_memcmp";
275     args[j++] = "-Wl,--wrap=OPENSSL_strcasecmp";
276     args[j++] = "-Wl,--wrap=OPENSSL_strncasecmp";
277     /* Frequently used libXML2 functions */
278     args[j++] = "-Wl,--wrap=xmlStrncmp";
279     args[j++] = "-Wl,--wrap=xmlStrcmp";
280     args[j++] = "-Wl,--wrap=xmlStrEqual";
281     args[j++] = "-Wl,--wrap=xmlStrcasecmp";
282     args[j++] = "-Wl,--wrap=xmlStrncasecmp";
283     args[j++] = "-Wl,--wrap=xmlStrstr";
284     args[j++] = "-Wl,--wrap=xmlStrcasestr";
285 
286     commonOpts(&j, args);
287 
288     for (int i = 1; i < argc; i++) {
289         args[j++] = argv[i];
290     }
291 
292     args[j++] = lhfuzzPath;
293     args[j++] = "-lpthread";
294 
295     return execCC(j, args);
296 }
297 
main(int argc,char ** argv)298 int main(int argc, char** argv) {
299     if (strstr(basename(util_StrDup(argv[0])), "++") != NULL) {
300         isCXX = true;
301     }
302     if (strstr(basename(util_StrDup(argv[0])), "-gcc") != NULL) {
303         isGCC = true;
304     }
305     if (strstr(basename(util_StrDup(argv[0])), "-g++") != NULL) {
306         isGCC = true;
307     }
308     if (argc <= 1) {
309         LOG_I("'%s': No arguments provided", argv[0]);
310         return execCC(argc, argv);
311     }
312     if (argc > (ARGS_MAX - 128)) {
313         LOG_F("'%s': Too many positional arguments: %d", argv[0], argc);
314         return EXIT_FAILURE;
315     }
316 
317     if (isLDMode(argc, argv)) {
318         return ldMode(argc, argv);
319     }
320     return ccMode(argc, argv);
321 }
322