]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | #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. | |
96 | extern "C" { | |
97 | int 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. | |
102 | static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; | |
103 | extern "C" int __afl_persistent_loop(unsigned int); | |
104 | static volatile char suppress_warning2 = AFL_PERSISTENT[0]; | |
105 | ||
106 | // Notify AFL about deferred forkserver. | |
107 | static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; | |
108 | extern "C" void __afl_manual_init(); | |
109 | static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; | |
110 | ||
111 | // Input buffer. | |
112 | static const size_t kMaxAflInputSize = 1 << 20; | |
113 | static uint8_t AflInputBuf[kMaxAflInputSize]; | |
114 | ||
115 | // Variables we need for writing to the extra stats file. | |
116 | static FILE *extra_stats_file = NULL; | |
117 | static uint32_t previous_peak_rss = 0; | |
118 | static time_t slowest_unit_time_secs = 0; | |
119 | static const int kNumExtraStats = 2; | |
120 | static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n" | |
121 | "slowest_unit_time_sec : %u\n"; | |
122 | ||
123 | // Copied from FuzzerUtil.cpp. | |
124 | size_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 | |
140 | static 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. | |
153 | static 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. | |
169 | static 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. | |
187 | static 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. | |
234 | static 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. | |
254 | extern "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. | |
260 | int 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 | ||
278 | int 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 | } |