xref: /netbsd-src/sys/external/bsd/compiler_rt/dist/lib/fuzzer/afl/afl_driver.cpp (revision a7c257b03e4462df2b1020128fb82716512d7856)
1 //===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //===----------------------------------------------------------------------===//
8 
9 /* This file allows to fuzz libFuzzer-style target functions
10  (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
11 
12 Usage:
13 ################################################################################
14 cat << EOF > test_fuzzer.cc
15 #include <stddef.h>
16 #include <stdint.h>
17 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
18   if (size > 0 && data[0] == 'H')
19     if (size > 1 && data[1] == 'I')
20        if (size > 2 && data[2] == '!')
21        __builtin_trap();
22   return 0;
23 }
24 EOF
25 # Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
26 clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
27 # Build afl-llvm-rt.o.c from the AFL distribution.
28 clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
29 # Build this file, link it with afl-llvm-rt.o.o and the target code.
30 clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
31 # Run AFL:
32 rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
33 $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
34 ################################################################################
35 Environment Variables:
36 There are a few environment variables that can be set to use features that
37 afl-fuzz doesn't have.
38 
39 AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
40 specified. If the file does not exist, it is created. This is useful for getting
41 stack traces (when using ASAN for example) or original error messages on hard to
42 reproduce bugs.
43 
44 AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
45 statistics to the file specified. Currently these are peak_rss_mb
46 (the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
47 the file does not exist it is created. If the file does exist then
48 afl_driver assumes it was restarted by afl-fuzz and will try to read old
49 statistics from the file. If that fails then the process will quit.
50 
51 */
52 #include <assert.h>
53 #include <errno.h>
54 #include <signal.h>
55 #include <stdint.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <sys/resource.h>
60 #include <sys/time.h>
61 #include <unistd.h>
62 
63 #include <fstream>
64 #include <iostream>
65 #include <vector>
66 
67 // Platform detection. Copied from FuzzerInternal.h
68 #ifdef __linux__
69 #define LIBFUZZER_LINUX 1
70 #define LIBFUZZER_APPLE 0
71 #define LIBFUZZER_NETBSD 0
72 #define LIBFUZZER_FREEBSD 0
73 #define LIBFUZZER_OPENBSD 0
74 #elif __APPLE__
75 #define LIBFUZZER_LINUX 0
76 #define LIBFUZZER_APPLE 1
77 #define LIBFUZZER_NETBSD 0
78 #define LIBFUZZER_FREEBSD 0
79 #define LIBFUZZER_OPENBSD 0
80 #elif __NetBSD__
81 #define LIBFUZZER_LINUX 0
82 #define LIBFUZZER_APPLE 0
83 #define LIBFUZZER_NETBSD 1
84 #define LIBFUZZER_FREEBSD 0
85 #define LIBFUZZER_OPENBSD 0
86 #elif __FreeBSD__
87 #define LIBFUZZER_LINUX 0
88 #define LIBFUZZER_APPLE 0
89 #define LIBFUZZER_NETBSD 0
90 #define LIBFUZZER_FREEBSD 1
91 #define LIBFUZZER_OPENBSD 0
92 #elif __OpenBSD__
93 #define LIBFUZZER_LINUX 0
94 #define LIBFUZZER_APPLE 0
95 #define LIBFUZZER_NETBSD 0
96 #define LIBFUZZER_FREEBSD 0
97 #define LIBFUZZER_OPENBSD 1
98 #else
99 #error "Support for your platform has not been implemented"
100 #endif
101 
102 // Used to avoid repeating error checking boilerplate. If cond is false, a
103 // fatal error has occurred in the program. In this event print error_message
104 // to stderr and abort(). Otherwise do nothing. Note that setting
105 // AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
106 // to the file as well, if the error occurs after the duplication is performed.
107 #define CHECK_ERROR(cond, error_message)                                       \
108   if (!(cond)) {                                                               \
109     fprintf(stderr, "%s\n", (error_message));                                  \
110     abort();                                                                   \
111   }
112 
113 // libFuzzer interface is thin, so we don't include any libFuzzer headers.
114 extern "C" {
115 int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
116 __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
117 }
118 
119 // Notify AFL about persistent mode.
120 static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
121 extern "C" int __afl_persistent_loop(unsigned int);
122 static volatile char suppress_warning2 = AFL_PERSISTENT[0];
123 
124 // Notify AFL about deferred forkserver.
125 static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
126 extern "C" void  __afl_manual_init();
127 static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
128 
129 // Input buffer.
130 static const size_t kMaxAflInputSize = 1 << 20;
131 static uint8_t AflInputBuf[kMaxAflInputSize];
132 
133 // Variables we need for writing to the extra stats file.
134 static FILE *extra_stats_file = NULL;
135 static uint32_t previous_peak_rss = 0;
136 static time_t slowest_unit_time_secs = 0;
137 static const int kNumExtraStats = 2;
138 static const char *kExtraStatsFormatString = "peak_rss_mb            : %u\n"
139                                              "slowest_unit_time_sec  : %u\n";
140 
141 // Experimental feature to use afl_driver without AFL's deferred mode.
142 // Needs to run before __afl_auto_init.
__decide_deferred_forkserver(void)143 __attribute__((constructor(0))) void __decide_deferred_forkserver(void) {
144   if (getenv("AFL_DRIVER_DONT_DEFER")) {
145     if (unsetenv("__AFL_DEFER_FORKSRV")) {
146       perror("Failed to unset __AFL_DEFER_FORKSRV");
147       abort();
148     }
149   }
150 }
151 
152 // Copied from FuzzerUtil.cpp.
GetPeakRSSMb()153 size_t GetPeakRSSMb() {
154   struct rusage usage;
155   if (getrusage(RUSAGE_SELF, &usage))
156     return 0;
157   if (LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD ||
158       LIBFUZZER_OPENBSD) {
159     // ru_maxrss is in KiB
160     return usage.ru_maxrss >> 10;
161   } else if (LIBFUZZER_APPLE) {
162     // ru_maxrss is in bytes
163     return usage.ru_maxrss >> 20;
164   }
165   assert(0 && "GetPeakRSSMb() is not implemented for your platform");
166   return 0;
167 }
168 
169 // Based on SetSigaction in FuzzerUtil.cpp
SetSigaction(int signum,void (* callback)(int,siginfo_t *,void *))170 static void SetSigaction(int signum,
171                          void (*callback)(int, siginfo_t *, void *)) {
172   struct sigaction sigact;
173   memset(&sigact, 0, sizeof(sigact));
174   sigact.sa_sigaction = callback;
175   if (sigaction(signum, &sigact, 0)) {
176     fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
177     exit(1);
178   }
179 }
180 
181 // Write extra stats to the file specified by the user. If none is specified
182 // this function will never be called.
write_extra_stats()183 static void write_extra_stats() {
184   uint32_t peak_rss = GetPeakRSSMb();
185 
186   if (peak_rss < previous_peak_rss)
187     peak_rss = previous_peak_rss;
188 
189   int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
190                               peak_rss, slowest_unit_time_secs);
191 
192   CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
193 
194   CHECK_ERROR(fclose(extra_stats_file) == 0,
195               "Failed to close extra_stats_file");
196 }
197 
198 // Call write_extra_stats before we exit.
crash_handler(int,siginfo_t *,void *)199 static void crash_handler(int, siginfo_t *, void *) {
200   // Make sure we don't try calling write_extra_stats again if we crashed while
201   // trying to call it.
202   static bool first_crash = true;
203   CHECK_ERROR(first_crash,
204               "Crashed in crash signal handler. This is a bug in the fuzzer.");
205 
206   first_crash = false;
207   write_extra_stats();
208 }
209 
210 // If the user has specified an extra_stats_file through the environment
211 // variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
212 // to write stats to it on exit. If no file is specified, do nothing. Otherwise
213 // install signal and exit handlers to write to the file when the process exits.
214 // Then if the file doesn't exist create it and set extra stats to 0. But if it
215 // does exist then read the initial values of the extra stats from the file
216 // and check that the file is writable.
maybe_initialize_extra_stats()217 static void maybe_initialize_extra_stats() {
218   // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
219   char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
220   if (!extra_stats_filename)
221     return;
222 
223   // Open the file and find the previous peak_rss_mb value.
224   // This is necessary because the fuzzing process is restarted after N
225   // iterations are completed. So we may need to get this value from a previous
226   // process to be accurate.
227   extra_stats_file = fopen(extra_stats_filename, "r");
228 
229   // If extra_stats_file already exists: read old stats from it.
230   if (extra_stats_file) {
231     int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
232                          &previous_peak_rss, &slowest_unit_time_secs);
233 
234     // Make sure we have read a real extra stats file and that we have used it
235     // to set slowest_unit_time_secs and previous_peak_rss.
236     CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
237 
238     CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
239 
240     // Now open the file for writing.
241     extra_stats_file = fopen(extra_stats_filename, "w");
242     CHECK_ERROR(extra_stats_file,
243                 "Failed to open extra stats file for writing");
244   } else {
245     // Looks like this is the first time in a fuzzing job this is being called.
246     extra_stats_file = fopen(extra_stats_filename, "w+");
247     CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
248   }
249 
250   // Make sure that crash_handler gets called on any kind of fatal error.
251   int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE,  SIGINT,
252                          SIGTERM};
253 
254   const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
255 
256   for (size_t idx = 0; idx < num_signals; idx++)
257     SetSigaction(crash_signals[idx], crash_handler);
258 
259   // Make sure it gets called on other kinds of exits.
260   atexit(write_extra_stats);
261 }
262 
263 // If the user asks us to duplicate stderr, then do it.
maybe_duplicate_stderr()264 static void maybe_duplicate_stderr() {
265   char* stderr_duplicate_filename =
266       getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
267 
268   if (!stderr_duplicate_filename)
269     return;
270 
271   FILE* stderr_duplicate_stream =
272       freopen(stderr_duplicate_filename, "a+", stderr);
273 
274   if (!stderr_duplicate_stream) {
275     fprintf(
276         stderr,
277         "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
278     abort();
279   }
280 }
281 
282 // Define LLVMFuzzerMutate to avoid link failures for targets that use it
283 // with libFuzzer's LLVMFuzzerCustomMutator.
LLVMFuzzerMutate(uint8_t * Data,size_t Size,size_t MaxSize)284 extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
285   assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
286   return 0;
287 }
288 
289 // Execute any files provided as parameters.
ExecuteFilesOnyByOne(int argc,char ** argv)290 int ExecuteFilesOnyByOne(int argc, char **argv) {
291   for (int i = 1; i < argc; i++) {
292     std::ifstream in(argv[i], std::ios::binary);
293     in.seekg(0, in.end);
294     size_t length = in.tellg();
295     in.seekg (0, in.beg);
296     std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
297     // Allocate exactly length bytes so that we reliably catch buffer overflows.
298     std::vector<char> bytes(length);
299     in.read(bytes.data(), bytes.size());
300     assert(in);
301     LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
302                            bytes.size());
303     std::cout << "Execution successful" << std::endl;
304   }
305   return 0;
306 }
307 
main(int argc,char ** argv)308 int main(int argc, char **argv) {
309   fprintf(stderr,
310       "======================= INFO =========================\n"
311       "This binary is built for AFL-fuzz.\n"
312       "To run the target function on individual input(s) execute this:\n"
313       "  %s < INPUT_FILE\n"
314       "or\n"
315       "  %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
316       "To fuzz with afl-fuzz execute this:\n"
317       "  afl-fuzz [afl-flags] %s [-N]\n"
318       "afl-fuzz will run N iterations before "
319       "re-spawning the process (default: 1000)\n"
320       "======================================================\n",
321           argv[0], argv[0], argv[0]);
322   if (LLVMFuzzerInitialize)
323     LLVMFuzzerInitialize(&argc, &argv);
324   // Do any other expensive one-time initialization here.
325 
326   maybe_duplicate_stderr();
327   maybe_initialize_extra_stats();
328 
329   if (!getenv("AFL_DRIVER_DONT_DEFER"))
330     __afl_manual_init();
331 
332   int N = 1000;
333   if (argc == 2 && argv[1][0] == '-')
334       N = atoi(argv[1] + 1);
335   else if(argc == 2 && (N = atoi(argv[1])) > 0)
336       fprintf(stderr, "WARNING: using the deprecated call style `%s %d`\n",
337               argv[0], N);
338   else if (argc > 1)
339     return ExecuteFilesOnyByOne(argc, argv);
340 
341   assert(N > 0);
342 
343   // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
344   // on the first execution of LLVMFuzzerTestOneInput is ignored.
345   uint8_t dummy_input[1] = {0};
346   LLVMFuzzerTestOneInput(dummy_input, 1);
347 
348   time_t unit_time_secs;
349   int num_runs = 0;
350   while (__afl_persistent_loop(N)) {
351     ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
352     if (n_read > 0) {
353       // Copy AflInputBuf into a separate buffer to let asan find buffer
354       // overflows. Don't use unique_ptr/etc to avoid extra dependencies.
355       uint8_t *copy = new uint8_t[n_read];
356       memcpy(copy, AflInputBuf, n_read);
357 
358       struct timeval unit_start_time;
359       CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
360                   "Calling gettimeofday failed");
361 
362       num_runs++;
363       LLVMFuzzerTestOneInput(copy, n_read);
364 
365       struct timeval unit_stop_time;
366       CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
367                   "Calling gettimeofday failed");
368 
369       // Update slowest_unit_time_secs if we see a new max.
370       unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
371       if (slowest_unit_time_secs < unit_time_secs)
372         slowest_unit_time_secs = unit_time_secs;
373 
374       delete[] copy;
375     }
376   }
377   fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
378 }
379