blob: 9f50dddc552f035221d390cdeb86beed1b7d236a [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
kamiledcfbba2017-08-30 22:44:11 +000071#define LIBFUZZER_NETBSD 0
kamil21423232018-01-12 17:15:05 +000072#define LIBFUZZER_FREEBSD 0
vitalybuka5f3206d2018-04-09 22:38:26 +000073#define LIBFUZZER_OPENBSD 0
george.karpenkov29efa6d2017-08-21 23:25:50 +000074#elif __APPLE__
75#define LIBFUZZER_LINUX 0
76#define LIBFUZZER_APPLE 1
kamiledcfbba2017-08-30 22:44:11 +000077#define LIBFUZZER_NETBSD 0
kamil21423232018-01-12 17:15:05 +000078#define LIBFUZZER_FREEBSD 0
vitalybuka5f3206d2018-04-09 22:38:26 +000079#define LIBFUZZER_OPENBSD 0
kamiledcfbba2017-08-30 22:44:11 +000080#elif __NetBSD__
81#define LIBFUZZER_LINUX 0
82#define LIBFUZZER_APPLE 0
83#define LIBFUZZER_NETBSD 1
kamil21423232018-01-12 17:15:05 +000084#define LIBFUZZER_FREEBSD 0
vitalybuka5f3206d2018-04-09 22:38:26 +000085#define LIBFUZZER_OPENBSD 0
kamil21423232018-01-12 17:15:05 +000086#elif __FreeBSD__
87#define LIBFUZZER_LINUX 0
88#define LIBFUZZER_APPLE 0
89#define LIBFUZZER_NETBSD 0
90#define LIBFUZZER_FREEBSD 1
vitalybuka5f3206d2018-04-09 22:38:26 +000091#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
george.karpenkov29efa6d2017-08-21 23:25:50 +000098#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
sylvestrea9eb8572018-03-13 14:35:10 +0000103// fatal error has occurred in the program. In this event print error_message
george.karpenkov29efa6d2017-08-21 23:25:50 +0000104// 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)) { \
morehouseba2c1cd2017-12-13 22:02:44 +0000109 fprintf(stderr, "%s\n", (error_message)); \
george.karpenkov29efa6d2017-08-21 23:25:50 +0000110 abort(); \
111 }
112
113// libFuzzer interface is thin, so we don't include any libFuzzer headers.
114extern "C" {
115int 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.
120static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
121extern "C" int __afl_persistent_loop(unsigned int);
122static volatile char suppress_warning2 = AFL_PERSISTENT[0];
123
124// Notify AFL about deferred forkserver.
125static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
126extern "C" void __afl_manual_init();
127static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
128
129// Input buffer.
130static const size_t kMaxAflInputSize = 1 << 20;
131static uint8_t AflInputBuf[kMaxAflInputSize];
132
133// Variables we need for writing to the extra stats file.
134static FILE *extra_stats_file = NULL;
135static uint32_t previous_peak_rss = 0;
136static time_t slowest_unit_time_secs = 0;
137static const int kNumExtraStats = 2;
138static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
139 "slowest_unit_time_sec : %u\n";
140
141// Copied from FuzzerUtil.cpp.
142size_t GetPeakRSSMb() {
143 struct rusage usage;
144 if (getrusage(RUSAGE_SELF, &usage))
145 return 0;
vitalybuka5f3206d2018-04-09 22:38:26 +0000146 if (LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD ||
147 LIBFUZZER_OPENBSD) {
george.karpenkov29efa6d2017-08-21 23:25:50 +0000148 // ru_maxrss is in KiB
149 return usage.ru_maxrss >> 10;
150 } else if (LIBFUZZER_APPLE) {
151 // ru_maxrss is in bytes
152 return usage.ru_maxrss >> 20;
153 }
154 assert(0 && "GetPeakRSSMb() is not implemented for your platform");
155 return 0;
156}
157
158// Based on SetSigaction in FuzzerUtil.cpp
159static void SetSigaction(int signum,
160 void (*callback)(int, siginfo_t *, void *)) {
161 struct sigaction sigact;
162 memset(&sigact, 0, sizeof(sigact));
163 sigact.sa_sigaction = callback;
164 if (sigaction(signum, &sigact, 0)) {
165 fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
166 exit(1);
167 }
168}
169
170// Write extra stats to the file specified by the user. If none is specified
171// this function will never be called.
172static void write_extra_stats() {
173 uint32_t peak_rss = GetPeakRSSMb();
174
175 if (peak_rss < previous_peak_rss)
176 peak_rss = previous_peak_rss;
177
178 int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
179 peak_rss, slowest_unit_time_secs);
180
181 CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
182
183 CHECK_ERROR(fclose(extra_stats_file) == 0,
184 "Failed to close extra_stats_file");
185}
186
187// Call write_extra_stats before we exit.
188static void crash_handler(int, siginfo_t *, void *) {
189 // Make sure we don't try calling write_extra_stats again if we crashed while
190 // trying to call it.
191 static bool first_crash = true;
192 CHECK_ERROR(first_crash,
193 "Crashed in crash signal handler. This is a bug in the fuzzer.");
194
195 first_crash = false;
196 write_extra_stats();
197}
198
199// If the user has specified an extra_stats_file through the environment
200// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
201// to write stats to it on exit. If no file is specified, do nothing. Otherwise
202// install signal and exit handlers to write to the file when the process exits.
203// Then if the file doesn't exist create it and set extra stats to 0. But if it
204// does exist then read the initial values of the extra stats from the file
205// and check that the file is writable.
206static void maybe_initialize_extra_stats() {
207 // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
208 char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
209 if (!extra_stats_filename)
210 return;
211
212 // Open the file and find the previous peak_rss_mb value.
213 // This is necessary because the fuzzing process is restarted after N
214 // iterations are completed. So we may need to get this value from a previous
215 // process to be accurate.
216 extra_stats_file = fopen(extra_stats_filename, "r");
217
218 // If extra_stats_file already exists: read old stats from it.
219 if (extra_stats_file) {
220 int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
221 &previous_peak_rss, &slowest_unit_time_secs);
222
223 // Make sure we have read a real extra stats file and that we have used it
224 // to set slowest_unit_time_secs and previous_peak_rss.
225 CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
226
227 CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
228
229 // Now open the file for writing.
230 extra_stats_file = fopen(extra_stats_filename, "w");
231 CHECK_ERROR(extra_stats_file,
232 "Failed to open extra stats file for writing");
233 } else {
234 // Looks like this is the first time in a fuzzing job this is being called.
235 extra_stats_file = fopen(extra_stats_filename, "w+");
236 CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
237 }
238
239 // Make sure that crash_handler gets called on any kind of fatal error.
240 int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
241 SIGTERM};
242
243 const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
244
245 for (size_t idx = 0; idx < num_signals; idx++)
246 SetSigaction(crash_signals[idx], crash_handler);
247
248 // Make sure it gets called on other kinds of exits.
249 atexit(write_extra_stats);
250}
251
252// If the user asks us to duplicate stderr, then do it.
253static void maybe_duplicate_stderr() {
254 char* stderr_duplicate_filename =
255 getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
256
257 if (!stderr_duplicate_filename)
258 return;
259
260 FILE* stderr_duplicate_stream =
261 freopen(stderr_duplicate_filename, "a+", stderr);
262
263 if (!stderr_duplicate_stream) {
264 fprintf(
265 stderr,
266 "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
267 abort();
268 }
269}
270
271// Define LLVMFuzzerMutate to avoid link failures for targets that use it
272// with libFuzzer's LLVMFuzzerCustomMutator.
273extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
274 assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
275 return 0;
276}
277
278// Execute any files provided as parameters.
279int ExecuteFilesOnyByOne(int argc, char **argv) {
280 for (int i = 1; i < argc; i++) {
281 std::ifstream in(argv[i]);
282 in.seekg(0, in.end);
283 size_t length = in.tellg();
284 in.seekg (0, in.beg);
285 std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
286 // Allocate exactly length bytes so that we reliably catch buffer overflows.
287 std::vector<char> bytes(length);
288 in.read(bytes.data(), bytes.size());
289 assert(in);
290 LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
291 bytes.size());
sylvestrea9eb8572018-03-13 14:35:10 +0000292 std::cout << "Execution successful" << std::endl;
george.karpenkov29efa6d2017-08-21 23:25:50 +0000293 }
294 return 0;
295}
296
297int main(int argc, char **argv) {
298 fprintf(stderr,
299 "======================= INFO =========================\n"
300 "This binary is built for AFL-fuzz.\n"
301 "To run the target function on individual input(s) execute this:\n"
302 " %s < INPUT_FILE\n"
303 "or\n"
304 " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
305 "To fuzz with afl-fuzz execute this:\n"
306 " afl-fuzz [afl-flags] %s [-N]\n"
307 "afl-fuzz will run N iterations before "
308 "re-spawning the process (default: 1000)\n"
309 "======================================================\n",
310 argv[0], argv[0], argv[0]);
311 if (LLVMFuzzerInitialize)
312 LLVMFuzzerInitialize(&argc, &argv);
313 // Do any other expensive one-time initialization here.
314
315 maybe_duplicate_stderr();
316 maybe_initialize_extra_stats();
317
318 __afl_manual_init();
319
320 int N = 1000;
321 if (argc == 2 && argv[1][0] == '-')
322 N = atoi(argv[1] + 1);
323 else if(argc == 2 && (N = atoi(argv[1])) > 0)
324 fprintf(stderr, "WARNING: using the deprecated call style `%s %d`\n",
325 argv[0], N);
326 else if (argc > 1)
327 return ExecuteFilesOnyByOne(argc, argv);
328
329 assert(N > 0);
morehouseba2c1cd2017-12-13 22:02:44 +0000330
331 // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
332 // on the first execution of LLVMFuzzerTestOneInput is ignored.
333 uint8_t dummy_input[1] = {0};
334 LLVMFuzzerTestOneInput(dummy_input, 1);
335
george.karpenkov29efa6d2017-08-21 23:25:50 +0000336 time_t unit_time_secs;
337 int num_runs = 0;
338 while (__afl_persistent_loop(N)) {
339 ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
340 if (n_read > 0) {
341 // Copy AflInputBuf into a separate buffer to let asan find buffer
342 // overflows. Don't use unique_ptr/etc to avoid extra dependencies.
343 uint8_t *copy = new uint8_t[n_read];
344 memcpy(copy, AflInputBuf, n_read);
345
346 struct timeval unit_start_time;
347 CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
348 "Calling gettimeofday failed");
349
350 num_runs++;
351 LLVMFuzzerTestOneInput(copy, n_read);
352
353 struct timeval unit_stop_time;
354 CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
355 "Calling gettimeofday failed");
356
357 // Update slowest_unit_time_secs if we see a new max.
358 unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
359 if (slowest_unit_time_secs < unit_time_secs)
360 slowest_unit_time_secs = unit_time_secs;
361
362 delete[] copy;
363 }
364 }
365 fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
366}