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 <stdint.h> 16#include <stddef.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 using fresh clang. 26clang -g -fsanitize-coverage=trace-pc 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 <stdio.h> 54#include <stdint.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58#include <errno.h> 59#include <signal.h> 60#include <sys/resource.h> 61#include <sys/time.h> 62// Platform detection. Copied from FuzzerInternal.h 63#ifdef __linux__ 64#define LIBFUZZER_LINUX 1 65#define LIBFUZZER_APPLE 0 66#elif __APPLE__ 67#define LIBFUZZER_LINUX 0 68#define LIBFUZZER_APPLE 1 69#else 70#error "Support for your platform has not been implemented" 71#endif 72 73// Used to avoid repeating error checking boilerplate. If cond is false, a 74// fatal error has occured in the program. In this event print error_message 75// to stderr and abort(). Otherwise do nothing. Note that setting 76// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended 77// to the file as well, if the error occurs after the duplication is performed. 78#define CHECK_ERROR(cond, error_message) \ 79 if (!(cond)) { \ 80 fprintf(stderr, (error_message)); \ 81 abort(); \ 82 } 83 84// libFuzzer interface is thin, so we don't include any libFuzzer headers. 85extern "C" { 86int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); 87__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); 88} 89 90// Notify AFL about persistent mode. 91static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; 92extern "C" int __afl_persistent_loop(unsigned int); 93static volatile char suppress_warning2 = AFL_PERSISTENT[0]; 94 95// Notify AFL about deferred forkserver. 96static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; 97extern "C" void __afl_manual_init(); 98static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; 99 100// Input buffer. 101static const size_t kMaxAflInputSize = 1 << 20; 102static uint8_t AflInputBuf[kMaxAflInputSize]; 103 104// Variables we need for writing to the extra stats file. 105static FILE *extra_stats_file = NULL; 106static uint32_t previous_peak_rss = 0; 107static time_t slowest_unit_time_secs = 0; 108static const int kNumExtraStats = 2; 109static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n" 110 "slowest_unit_time_sec : %u\n"; 111 112// Copied from FuzzerUtil.cpp. 113size_t GetPeakRSSMb() { 114 struct rusage usage; 115 if (getrusage(RUSAGE_SELF, &usage)) 116 return 0; 117 if (LIBFUZZER_LINUX) { 118 // ru_maxrss is in KiB 119 return usage.ru_maxrss >> 10; 120 } else if (LIBFUZZER_APPLE) { 121 // ru_maxrss is in bytes 122 return usage.ru_maxrss >> 20; 123 } 124 assert(0 && "GetPeakRSSMb() is not implemented for your platform"); 125 return 0; 126} 127 128// Based on SetSigaction in FuzzerUtil.cpp 129static void SetSigaction(int signum, 130 void (*callback)(int, siginfo_t *, void *)) { 131 struct sigaction sigact; 132 memset(&sigact, 0, sizeof(sigact)); 133 sigact.sa_sigaction = callback; 134 if (sigaction(signum, &sigact, 0)) { 135 fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno); 136 exit(1); 137 } 138} 139 140// Write extra stats to the file specified by the user. If none is specified 141// this function will never be called. 142static void write_extra_stats() { 143 uint32_t peak_rss = GetPeakRSSMb(); 144 145 if (peak_rss < previous_peak_rss) 146 peak_rss = previous_peak_rss; 147 148 int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString, 149 peak_rss, slowest_unit_time_secs); 150 151 CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file"); 152 153 CHECK_ERROR(fclose(extra_stats_file) == 0, 154 "Failed to close extra_stats_file"); 155} 156 157// Call write_extra_stats before we exit. 158static void crash_handler(int, siginfo_t *, void *) { 159 // Make sure we don't try calling write_extra_stats again if we crashed while 160 // trying to call it. 161 static bool first_crash = true; 162 CHECK_ERROR(first_crash, 163 "Crashed in crash signal handler. This is a bug in the fuzzer."); 164 165 first_crash = false; 166 write_extra_stats(); 167} 168 169// If the user has specified an extra_stats_file through the environment 170// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up 171// to write stats to it on exit. If no file is specified, do nothing. Otherwise 172// install signal and exit handlers to write to the file when the process exits. 173// Then if the file doesn't exist create it and set extra stats to 0. But if it 174// does exist then read the initial values of the extra stats from the file 175// and check that the file is writable. 176static void maybe_initialize_extra_stats() { 177 // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do. 178 char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME"); 179 if (!extra_stats_filename) 180 return; 181 182 // Open the file and find the previous peak_rss_mb value. 183 // This is necessary because the fuzzing process is restarted after N 184 // iterations are completed. So we may need to get this value from a previous 185 // process to be accurate. 186 extra_stats_file = fopen(extra_stats_filename, "r"); 187 188 // If extra_stats_file already exists: read old stats from it. 189 if (extra_stats_file) { 190 int matches = fscanf(extra_stats_file, kExtraStatsFormatString, 191 &previous_peak_rss, &slowest_unit_time_secs); 192 193 // Make sure we have read a real extra stats file and that we have used it 194 // to set slowest_unit_time_secs and previous_peak_rss. 195 CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt"); 196 197 CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file"); 198 199 // Now open the file for writing. 200 extra_stats_file = fopen(extra_stats_filename, "w"); 201 CHECK_ERROR(extra_stats_file, 202 "Failed to open extra stats file for writing"); 203 } else { 204 // Looks like this is the first time in a fuzzing job this is being called. 205 extra_stats_file = fopen(extra_stats_filename, "w+"); 206 CHECK_ERROR(extra_stats_file, "failed to create extra stats file"); 207 } 208 209 // Make sure that crash_handler gets called on any kind of fatal error. 210 int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT, 211 SIGTERM}; 212 213 const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]); 214 215 for (size_t idx = 0; idx < num_signals; idx++) 216 SetSigaction(crash_signals[idx], crash_handler); 217 218 // Make sure it gets called on other kinds of exits. 219 atexit(write_extra_stats); 220} 221 222// If the user asks us to duplicate stderr, then do it. 223static void maybe_duplicate_stderr() { 224 char* stderr_duplicate_filename = 225 getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); 226 227 if (!stderr_duplicate_filename) 228 return; 229 230 FILE* stderr_duplicate_stream = 231 freopen(stderr_duplicate_filename, "a+", stderr); 232 233 if (!stderr_duplicate_stream) { 234 fprintf( 235 stderr, 236 "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); 237 abort(); 238 } 239} 240 241int main(int argc, char **argv) { 242 fprintf(stderr, "======================= INFO =========================\n" 243 "This binary is built for AFL-fuzz.\n" 244 "To run the target function on a single input execute this:\n" 245 " %s < INPUT_FILE\n" 246 "To run the fuzzing execute this:\n" 247 " afl-fuzz [afl-flags] %s [N] " 248 "-- run N fuzzing iterations before " 249 "re-spawning the process (default: 1000)\n" 250 "======================================================\n", 251 argv[0], argv[0]); 252 if (LLVMFuzzerInitialize) 253 LLVMFuzzerInitialize(&argc, &argv); 254 // Do any other expensive one-time initialization here. 255 256 maybe_duplicate_stderr(); 257 maybe_initialize_extra_stats(); 258 259 __afl_manual_init(); 260 261 int N = 1000; 262 if (argc >= 2) 263 N = atoi(argv[1]); 264 assert(N > 0); 265 time_t unit_time_secs; 266 int num_runs = 0; 267 while (__afl_persistent_loop(N)) { 268 ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize); 269 if (n_read > 0) { 270 // Copy AflInputBuf into a separate buffer to let asan find buffer 271 // overflows. Don't use unique_ptr/etc to avoid extra dependencies. 272 uint8_t *copy = new uint8_t[n_read]; 273 memcpy(copy, AflInputBuf, n_read); 274 275 struct timeval unit_start_time; 276 CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0, 277 "Calling gettimeofday failed"); 278 279 num_runs++; 280 LLVMFuzzerTestOneInput(copy, n_read); 281 282 struct timeval unit_stop_time; 283 CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0, 284 "Calling gettimeofday failed"); 285 286 // Update slowest_unit_time_secs if we see a new max. 287 unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec; 288 if (slowest_unit_time_secs < unit_time_secs) 289 slowest_unit_time_secs = unit_time_secs; 290 291 delete[] copy; 292 } 293 } 294 fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs); 295} 296