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