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