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