1 /*
2    american fuzzy lop - wrapper for GCC and clang
3    ----------------------------------------------
4 
5    Written and maintained by Michal Zalewski <lcamtuf@google.com>
6 
7    Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
8 
9    Licensed under the Apache License, Version 2.0 (the "License");
10    you may not use this file except in compliance with the License.
11    You may obtain a copy of the License at:
12 
13      http://www.apache.org/licenses/LICENSE-2.0
14 
15    This program is a drop-in replacement for GCC or clang. The most common way
16    of using it is to pass the path to afl-gcc or afl-clang via CC when invoking
17    ./configure.
18 
19    (Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.)
20 
21    The wrapper needs to know the path to afl-as (renamed to 'as'). The default
22    is /usr/local/lib/afl/. A convenient way to specify alternative directories
23    would be to set AFL_PATH.
24 
25    If AFL_HARDEN is set, the wrapper will compile the target app with various
26    hardening options that may help detect memory management issues more
27    reliably. You can also specify AFL_USE_ASAN to enable ASAN.
28 
29    If you want to call a non-default compiler as a next step of the chain,
30    specify its location via AFL_CC or AFL_CXX.
31 
32  */
33 
34 #define AFL_MAIN
35 
36 #include "config.h"
37 #include "types.h"
38 #include "debug.h"
39 #include "alloc-inl.h"
40 
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 static u8*  as_path;                /* Path to the AFL 'as' wrapper      */
47 static u8** cc_params;              /* Parameters passed to the real CC  */
48 static u32  cc_par_cnt = 1;         /* Param count, including argv0      */
49 static u8   be_quiet,               /* Quiet mode                        */
50             clang_mode;             /* Invoked as afl-clang*?            */
51 
52 
53 /* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
54    from argv[0]. If that fails, abort. */
55 
find_as(u8 * argv0)56 static void find_as(u8* argv0) {
57 
58   u8 *afl_path = getenv("AFL_PATH");
59   u8 *slash, *tmp;
60 
61   if (afl_path) {
62 
63     tmp = alloc_printf("%s/as", afl_path);
64 
65     if (!access(tmp, X_OK)) {
66       as_path = afl_path;
67       ck_free(tmp);
68       return;
69     }
70 
71     ck_free(tmp);
72 
73   }
74 
75   slash = strrchr(argv0, '/');
76 
77   if (slash) {
78 
79     u8 *dir;
80 
81     *slash = 0;
82     dir = ck_strdup(argv0);
83     *slash = '/';
84 
85     tmp = alloc_printf("%s/afl-as", dir);
86 
87     if (!access(tmp, X_OK)) {
88       as_path = dir;
89       ck_free(tmp);
90       return;
91     }
92 
93     ck_free(tmp);
94     ck_free(dir);
95 
96   }
97 
98   if (!access(AFL_PATH "/as", X_OK)) {
99     as_path = AFL_PATH;
100     return;
101   }
102 
103   FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
104 
105 }
106 
107 
108 /* Copy argv to cc_params, making the necessary edits. */
109 
edit_params(u32 argc,char ** argv)110 static void edit_params(u32 argc, char** argv) {
111 
112   u8 fortify_set = 0, asan_set = 0;
113   u8 *name;
114 
115 #if defined(__FreeBSD__) && defined(__x86_64__)
116   u8 m32_set = 0;
117 #endif
118 
119   cc_params = ck_alloc((argc + 128) * sizeof(u8*));
120 
121   name = strrchr(argv[0], '/');
122   if (!name) name = argv[0]; else name++;
123 
124   if (!strncmp(name, "afl-clang", 9)) {
125 
126     clang_mode = 1;
127 
128     setenv(CLANG_ENV_VAR, "1", 1);
129 
130     if (!strcmp(name, "afl-clang++")) {
131       u8* alt_cxx = getenv("AFL_CXX");
132       cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
133     } else {
134       u8* alt_cc = getenv("AFL_CC");
135       cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
136     }
137 
138   } else {
139 
140     /* With GCJ and Eclipse installed, you can actually compile Java! The
141        instrumentation will work (amazingly). Alas, unhandled exceptions do
142        not call abort(), so afl-fuzz would need to be modified to equate
143        non-zero exit codes with crash conditions when working with Java
144        binaries. Meh. */
145 
146 #ifdef __APPLE__
147 
148     if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
149     else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
150     else cc_params[0] = getenv("AFL_CC");
151 
152     if (!cc_params[0]) {
153 
154       SAYF("\n" cLRD "[-] " cRST
155            "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
156            "    'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
157            "    set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
158 
159       FATAL("AFL_CC or AFL_CXX required on MacOS X");
160 
161     }
162 
163 #else
164 
165     if (!strcmp(name, "afl-g++")) {
166       u8* alt_cxx = getenv("AFL_CXX");
167       cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
168     } else if (!strcmp(name, "afl-gcj")) {
169       u8* alt_cc = getenv("AFL_GCJ");
170       cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
171     } else {
172       u8* alt_cc = getenv("AFL_CC");
173       cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
174     }
175 
176 #endif /* __APPLE__ */
177 
178   }
179 
180   while (--argc) {
181     u8* cur = *(++argv);
182 
183     if (!strncmp(cur, "-B", 2)) {
184 
185       if (!be_quiet) WARNF("-B is already set, overriding");
186 
187       if (!cur[2] && argc > 1) { argc--; argv++; }
188       continue;
189 
190     }
191 
192     if (!strcmp(cur, "-integrated-as")) continue;
193 
194     if (!strcmp(cur, "-pipe")) continue;
195 
196 #if defined(__FreeBSD__) && defined(__x86_64__)
197     if (!strcmp(cur, "-m32")) m32_set = 1;
198 #endif
199 
200     if (!strcmp(cur, "-fsanitize=address") ||
201         !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
202 
203     if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
204 
205     cc_params[cc_par_cnt++] = cur;
206 
207   }
208 
209   cc_params[cc_par_cnt++] = "-B";
210   cc_params[cc_par_cnt++] = as_path;
211 
212   if (clang_mode)
213     cc_params[cc_par_cnt++] = "-no-integrated-as";
214 
215   if (getenv("AFL_HARDEN")) {
216 
217     cc_params[cc_par_cnt++] = "-fstack-protector-all";
218 
219     if (!fortify_set)
220       cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
221 
222   }
223 
224   if (asan_set) {
225 
226     /* Pass this on to afl-as to adjust map density. */
227 
228     setenv("AFL_USE_ASAN", "1", 1);
229 
230   } else if (getenv("AFL_USE_ASAN")) {
231 
232     if (getenv("AFL_USE_MSAN"))
233       FATAL("ASAN and MSAN are mutually exclusive");
234 
235     if (getenv("AFL_HARDEN"))
236       FATAL("ASAN and AFL_HARDEN are mutually exclusive");
237 
238     cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
239     cc_params[cc_par_cnt++] = "-fsanitize=address";
240 
241   } else if (getenv("AFL_USE_MSAN")) {
242 
243     if (getenv("AFL_USE_ASAN"))
244       FATAL("ASAN and MSAN are mutually exclusive");
245 
246     if (getenv("AFL_HARDEN"))
247       FATAL("MSAN and AFL_HARDEN are mutually exclusive");
248 
249     cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
250     cc_params[cc_par_cnt++] = "-fsanitize=memory";
251 
252 
253   }
254 
255   if (!getenv("AFL_DONT_OPTIMIZE")) {
256 
257 #if defined(__FreeBSD__) && defined(__x86_64__)
258 
259     /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
260        works OK. This has nothing to do with us, but let's avoid triggering
261        that bug. */
262 
263     if (!clang_mode || !m32_set)
264       cc_params[cc_par_cnt++] = "-g";
265 
266 #else
267 
268       cc_params[cc_par_cnt++] = "-g";
269 
270 #endif
271 
272     cc_params[cc_par_cnt++] = "-O3";
273     cc_params[cc_par_cnt++] = "-funroll-loops";
274 
275     /* Two indicators that you're building for fuzzing; one of them is
276        AFL-specific, the other is shared with libfuzzer. */
277 
278     cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
279     cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
280 
281   }
282 
283   if (getenv("AFL_NO_BUILTIN")) {
284 
285     cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
286     cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
287     cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
288     cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
289     cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
290     cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
291     cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
292 
293   }
294 
295   cc_params[cc_par_cnt] = NULL;
296 
297 }
298 
299 
300 /* Main entry point */
301 
main(int argc,char ** argv)302 int main(int argc, char** argv) {
303 
304   if (isatty(2) && !getenv("AFL_QUIET")) {
305 
306     SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
307 
308   } else be_quiet = 1;
309 
310   if (argc < 2) {
311 
312     SAYF("\n"
313          "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
314          "for gcc or clang, letting you recompile third-party code with the required\n"
315          "runtime instrumentation. A common use pattern would be one of the following:\n\n"
316 
317          "  CC=%s/afl-gcc ./configure\n"
318          "  CXX=%s/afl-g++ ./configure\n\n"
319 
320          "You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
321          "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
322          BIN_PATH, BIN_PATH);
323 
324     exit(1);
325 
326   }
327 
328   find_as(argv[0]);
329 
330   edit_params(argc, argv);
331 
332   execvp(cc_params[0], (char**)cc_params);
333 
334   FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
335 
336   return 0;
337 
338 }
339