• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   american fuzzy lop++ - wrapper for llvm 11+ lld
3   -----------------------------------------------
4 
5   Written by Marc Heuse <mh@mh-sec.de> for afl++
6 
7   Maintained by Marc Heuse <mh@mh-sec.de>,
8                 Heiko Eißfeldt <heiko.eissfeldt@hexco.de>
9                 Andrea Fioraldi <andreafioraldi@gmail.com>
10                 Dominik Maier <domenukk@gmail.com>
11 
12   Copyright 2019-2022 AFLplusplus Project. All rights reserved.
13 
14   Licensed under the Apache License, Version 2.0 (the "License");
15   you may not use this file except in compliance with the License.
16   You may obtain a copy of the License at:
17 
18     https://www.apache.org/licenses/LICENSE-2.0
19 
20   The sole purpose of this wrapper is to preprocess clang LTO files when
21   linking with lld and performing the instrumentation on the whole program.
22 
23 */
24 
25 #define AFL_MAIN
26 #define _GNU_SOURCE
27 
28 #include "config.h"
29 #include "types.h"
30 #include "debug.h"
31 #include "alloc-inl.h"
32 
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40 
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/wait.h>
44 #include <sys/time.h>
45 
46 #include <dirent.h>
47 
48 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
49     defined(__DragonFly__)
50   #include <limits.h>
51 #endif
52 
53 #ifdef __APPLE__
54   #include <sys/syslimits.h>
55 #endif
56 
57 #define MAX_PARAM_COUNT 4096
58 
59 static u8 **ld_params;              /* Parameters passed to the real 'ld'   */
60 
61 static u8 *afl_path = AFL_PATH;
62 static u8 *real_ld = AFL_REAL_LD;
63 
64 static u8 be_quiet,                 /* Quiet mode (no stderr output)        */
65     debug,                          /* AFL_DEBUG                            */
66     passthrough,                    /* AFL_LD_PASSTHROUGH - no link+optimize*/
67     just_version;                   /* Just show version?                   */
68 
69 static u32 ld_param_cnt = 1;        /* Number of params to 'ld'             */
70 
71 /* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'.
72    Note that the file name is always the last parameter passed by GCC,
73    so we exploit this property to keep the code "simple". */
edit_params(int argc,char ** argv)74 static void edit_params(int argc, char **argv) {
75 
76   u32 i, gold_pos = 0, gold_present = 0, rt_present = 0, rt_lto_present = 0,
77          inst_present = 0;
78   char *ptr;
79 
80   ld_params = ck_alloc(4096 * sizeof(u8 *));
81 
82   ld_params[0] = (u8 *)real_ld;
83 
84   if (!passthrough) {
85 
86     for (i = 1; i < (u32)argc; i++) {
87 
88       if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1;
89       if (strstr(argv[i], "/afl-compiler-rt.o") != NULL) rt_present = 1;
90       if (strstr(argv[i], "/afl-llvm-lto-instr") != NULL) inst_present = 1;
91 
92     }
93 
94     for (i = 1; i < (u32)argc && !gold_pos; i++) {
95 
96       if (strcmp(argv[i], "-plugin") == 0) {
97 
98         if (strncmp(argv[i], "-plugin=", strlen("-plugin=")) == 0) {
99 
100           if (strcasestr(argv[i], "LLVMgold.so") != NULL)
101             gold_present = gold_pos = i + 1;
102 
103         } else if (i < (u32)argc &&
104 
105                    strcasestr(argv[i + 1], "LLVMgold.so") != NULL) {
106 
107           gold_present = gold_pos = i + 2;
108 
109         }
110 
111       }
112 
113     }
114 
115     if (!gold_pos) {
116 
117       for (i = 1; i + 1 < (u32)argc && !gold_pos; i++) {
118 
119         if (argv[i][0] != '-') {
120 
121           if (argv[i - 1][0] == '-') {
122 
123             switch (argv[i - 1][1]) {
124 
125               case 'b':
126                 break;
127               case 'd':
128                 break;
129               case 'e':
130                 break;
131               case 'F':
132                 break;
133               case 'f':
134                 break;
135               case 'I':
136                 break;
137               case 'l':
138                 break;
139               case 'L':
140                 break;
141               case 'm':
142                 break;
143               case 'o':
144                 break;
145               case 'O':
146                 break;
147               case 'p':
148                 if (index(argv[i - 1], '=') == NULL) gold_pos = i;
149                 break;
150               case 'R':
151                 break;
152               case 'T':
153                 break;
154               case 'u':
155                 break;
156               case 'y':
157                 break;
158               case 'z':
159                 break;
160               case '-': {
161 
162                 if (strcmp(argv[i - 1], "--oformat") == 0) break;
163                 if (strcmp(argv[i - 1], "--output") == 0) break;
164                 if (strncmp(argv[i - 1], "--opt-remarks-", 14) == 0) break;
165                 gold_pos = i;
166                 break;
167 
168               }
169 
170               default:
171                 gold_pos = i;
172 
173             }
174 
175           } else
176 
177             gold_pos = i;
178 
179         }
180 
181       }
182 
183     }
184 
185     if (!gold_pos) gold_pos = 1;
186 
187   }
188 
189   if (getenv("AFL_LLVM_INSTRIM") ||
190       ((ptr = getenv("AFL_LLVM_INSTRUMENT")) &&
191        (strcasestr(ptr, "CFG") == 0 || strcasestr(ptr, "INSTRIM") == 0)))
192     FATAL(
193         "InsTrim was removed because it is not effective. Use a modern LLVM "
194         "and PCGUARD (which is the default in afl-cc).\n");
195 
196   if (debug)
197     DEBUGF(
198         "passthrough=%s, gold_pos=%u, gold_present=%s "
199         "inst_present=%s rt_present=%s rt_lto_present=%s\n",
200         passthrough ? "true" : "false", gold_pos,
201         gold_present ? "true" : "false", inst_present ? "true" : "false",
202         rt_present ? "true" : "false", rt_lto_present ? "true" : "false");
203 
204   for (i = 1; i < (u32)argc; i++) {
205 
206     if (ld_param_cnt >= MAX_PARAM_COUNT)
207       FATAL(
208           "Too many command line parameters because of unpacking .a archives, "
209           "this would need to be done by hand ... sorry! :-(");
210 
211     if (strcmp(argv[i], "--afl") == 0) {
212 
213       if (!be_quiet) OKF("afl++ test command line flag detected, exiting.");
214       exit(0);
215 
216     }
217 
218     if (i == gold_pos && !passthrough) {
219 
220       ld_params[ld_param_cnt++] = alloc_printf("-L%s/../lib", LLVM_BINDIR);
221 
222       if (!gold_present) {
223 
224         ld_params[ld_param_cnt++] = "-plugin";
225         ld_params[ld_param_cnt++] =
226             alloc_printf("%s/../lib/LLVMgold.so", LLVM_BINDIR);
227 
228       }
229 
230       ld_params[ld_param_cnt++] = "--allow-multiple-definition";
231 
232       if (!inst_present) {
233 
234         ld_params[ld_param_cnt++] = alloc_printf(
235             "-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", afl_path);
236 
237       }
238 
239       if (!rt_present)
240         ld_params[ld_param_cnt++] =
241             alloc_printf("%s/afl-compiler-rt.o", afl_path);
242       if (!rt_lto_present)
243         ld_params[ld_param_cnt++] =
244             alloc_printf("%s/afl-llvm-rt-lto.o", afl_path);
245 
246     }
247 
248     ld_params[ld_param_cnt++] = argv[i];
249 
250   }
251 
252   ld_params[ld_param_cnt] = NULL;
253 
254 }
255 
256 /* Main entry point */
257 
main(int argc,char ** argv)258 int main(int argc, char **argv) {
259 
260   s32  pid, i, status;
261   char thecwd[PATH_MAX];
262 
263   if (getenv("AFL_LD_CALLER") != NULL) {
264 
265     FATAL("ld loop detected! Set AFL_REAL_LD!\n");
266 
267   }
268 
269   if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) {
270 
271     SAYF(cCYA "afl-ld-to" VERSION cRST
272               " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n");
273 
274   } else
275 
276     be_quiet = 1;
277 
278   if (getenv("AFL_DEBUG") != NULL) debug = 1;
279   if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH");
280   if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1;
281   if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD");
282 
283   if (!afl_path || !*afl_path) afl_path = "/usr/local/lib/afl";
284 
285   setenv("AFL_LD_CALLER", "1", 1);
286 
287   if (debug) {
288 
289     if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, ".");
290 
291     DEBUGF("cd \"%s\";", thecwd);
292     for (i = 0; i < argc; i++)
293       SAYF(" \"%s\"", argv[i]);
294     SAYF("\n");
295 
296   }
297 
298   if (argc < 2) {
299 
300     SAYF(
301         "\n"
302         "This is a helper application for afl-clang-lto.\n"
303         "It is a wrapper around llvm's 'lld' in case afl-clang-lto cannot be "
304         "used.\n"
305         "Note that the target still has to be compiled with -flto=full!\n"
306         "You probably don't want to run this program directly but rather pass "
307         "it as LD\nparameter to e.g. configure scripts.\n\n"
308 
309         "Environment variables:\n"
310         "  AFL_LD_PASSTHROUGH   do not link+optimize == no instrumentation\n"
311         "  AFL_REAL_LD          point to the real llvm 11 lld if necessary\n"
312 
313         "\nafl-ld-to was compiled with the fixed real 'ld' of %s and the "
314         "binary path of %s\n\n",
315         real_ld, LLVM_BINDIR);
316 
317     exit(1);
318 
319   }
320 
321   edit_params(argc, argv);  // here most of the magic happens :-)
322 
323   if (debug) {
324 
325     DEBUGF("cd \"%s\";", thecwd);
326     for (i = 0; i < (s32)ld_param_cnt; i++)
327       SAYF(" \"%s\"", ld_params[i]);
328     SAYF("\n");
329 
330   }
331 
332   if (!(pid = fork())) {
333 
334     if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params);
335     execvp("ld", (char **)ld_params);  // fallback
336     FATAL("Oops, failed to execute 'ld' - check your PATH");
337 
338   }
339 
340   if (pid < 0) PFATAL("fork() failed");
341 
342   if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
343   if (debug) DEBUGF("linker result: %d\n", status);
344 
345   if (!just_version) {
346 
347     if (status == 0) {
348 
349       if (!be_quiet) OKF("Linker was successful");
350 
351     } else {
352 
353       SAYF(cLRD "[-] " cRST
354                 "Linker failed, please investigate and send a bug report. Most "
355                 "likely an 'ld' option is incompatible with %s.\n",
356            AFL_CLANG_FLTO);
357 
358     }
359 
360   }
361 
362   exit(WEXITSTATUS(status));
363 
364 }
365 
366