blob: cc22fd5c3e4a8c9677b1915b17d61bf969ebf580 [file] [log] [blame]
/*
american fuzzy lop++ - fuzzer code
--------------------------------
Originally written by Michal Zalewski
Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com>
Copyright 2016, 2017 Google Inc. All rights reserved.
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This is the real deal: the program takes an instrumented binary and
attempts a variety of basic fuzzing tricks, paying close attention to
how they affect the execution path.
*/
#include "afl-fuzz.h"
u8 be_quiet = 0;
static u8 *get_libradamsa_path(u8 *own_loc) {
u8 *tmp, *cp, *rsl, *own_copy;
tmp = getenv("AFL_PATH");
if (tmp) {
cp = alloc_printf("%s/libradamsa.so", tmp);
if (access(cp, X_OK)) FATAL("Unable to find '%s'", cp);
return cp;
}
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
if (rsl) {
*rsl = 0;
cp = alloc_printf("%s/libradamsa.so", own_copy);
ck_free(own_copy);
if (!access(cp, X_OK)) return cp;
} else
ck_free(own_copy);
if (!access(AFL_PATH "/libradamsa.so", X_OK)) {
return ck_strdup(AFL_PATH "/libradamsa.so");
}
if (!access(BIN_PATH "/libradamsa.so", X_OK)) {
return ck_strdup(BIN_PATH "/libradamsa.so");
}
SAYF(
"\n" cLRD "[-] " cRST
"Oops, unable to find the 'libradamsa.so' binary. The binary must be "
"built\n"
" separately using 'make radamsa'. If you already have the binary "
"installed,\n you may need to specify AFL_PATH in the environment.\n");
FATAL("Failed to locate 'libradamsa.so'.");
}
/* Display usage hints. */
static void usage(afl_state_t *afl, u8 *argv0, int more_help) {
SAYF(
"\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n"
"Required parameters:\n"
" -i dir - input directory with test cases\n"
" -o dir - output directory for fuzzer findings\n\n"
"Execution control settings:\n"
" -p schedule - power schedules recompute a seed's performance "
"score.\n"
" <explore(default), fast, coe, lin, quad, exploit, "
"mmopt, rare>\n"
" see docs/power_schedules.md\n"
" -f file - location read by the fuzzed program (stdin)\n"
" -t msec - timeout for each run (auto-scaled, 50-%d ms)\n"
" -m megs - memory limit for child process (%d MB)\n"
" -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine "
"mode)\n\n"
"Mutator settings:\n"
" -R[R] - add Radamsa as mutator, add another -R to exclusivly "
"run it\n"
" -L minutes - use MOpt(imize) mode and set the limit time for "
"entering the\n"
" pacemaker mode (minutes of no new paths, 0 = "
"immediately).\n"
" a recommended value is 10-60. see "
"docs/README.MOpt.md\n"
" -c program - enable CmpLog by specifying a binary compiled for "
"it.\n"
" if using QEMU, just use -c 0.\n\n"
"Fuzzing behavior settings:\n"
" -N - do not unlink the fuzzing input file (only for "
"devices etc.!)\n"
" -d - quick & dirty mode (skips deterministic steps)\n"
" -n - fuzz without instrumentation (dumb mode)\n"
" -x dir - optional fuzzer dictionary (see README.md, its really "
"good!)\n\n"
"Testing settings:\n"
" -s seed - use a fixed seed for the RNG\n"
" -V seconds - fuzz for a maximum total time of seconds then "
"terminate\n"
" -E execs - fuzz for a maximum number of total executions then "
"terminate\n"
" Note: -V/-E are not precise, they are checked after a queue entry "
"is done\n which can be many minutes/execs later\n\n"
"Other stuff:\n"
" -T text - text banner to show on the screen\n"
" -M / -S id - distributed mode (see docs/parallel_fuzzing.md)\n"
" -I command - execute this command/script when a new crash is "
"found\n"
" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap "
"file\n"
" -C - crash exploration mode (the peruvian rabbit thing)\n"
" -e ext - File extension for the temporarily generated test "
"case\n\n",
argv0, EXEC_TIMEOUT, MEM_LIMIT);
if (more_help > 1)
SAYF(
"Environment variables used:\n"
"AFL_PATH: path to AFL support binaries\n"
"AFL_QUIET: suppress forkserver status messages\n"
"AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n"
"LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n"
"AFL_BENCH_JUST_ONE: run the target just once\n"
"AFL_DUMB_FORKSRV: use fork server without feedback from target\n"
"AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n"
"AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n"
"AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n"
"AFL_DEBUG: extra debugging output for Python mode trimming\n"
"AFL_DISABLE_TRIM: disable the trimming of test cases\n"
"AFL_NO_UI: switch status screen off\n"
"AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n"
"AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n"
"AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n"
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
"AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n"
"AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n"
"AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n"
"AFL_HANG_TMOUT: override timeout value (in milliseconds)\n"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
"AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n"
"AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n"
"AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n"
"AFL_POST_LIBRARY: postprocess generated test cases before use as target input\n"
"AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n"
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n"
"ASAN_OPTIONS: custom settings for ASAN\n"
" (must contain abort_on_error=1 and symbolize=0)\n"
"MSAN_OPTIONS: custom settings for MSAN\n"
" (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n"
"AFL_SKIP_BIN_CHECK: skip the check, if the target is an excutable\n"
//"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n"
//"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n"
"AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n"
"AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n"
"AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n"
"\n"
);
else
SAYF(
"To view also the supported environment variables of afl-fuzz please "
"use \"-hh\".\n\n");
#ifdef USE_PYTHON
SAYF("Compiled with %s module support, see docs/custom_mutator.md\n",
(char *)PYTHON_VERSION);
#endif
SAYF("For additional help please consult %s/README.md\n\n", doc_path);
exit(1);
#undef PHYTON_SUPPORT
}
#ifndef AFL_LIB
static int stricmp(char const *a, char const *b) {
for (;; ++a, ++b) {
int d;
d = tolower(*a) - tolower(*b);
if (d != 0 || !*a) return d;
}
}
/* Main entry point */
int main(int argc, char **argv_orig, char **envp) {
s32 opt;
u64 prev_queued = 0;
u32 sync_interval_cnt = 0, seek_to, show_help = 0;
u8 * extras_dir = 0;
u8 mem_limit_given = 0, exit_1 = 0;
char **use_argv;
struct timeval tv;
struct timezone tz;
char **argv = argv_cpy_dup(argc, argv_orig);
afl_state_t *afl = calloc(1, sizeof(afl_state_t));
if (!afl) { FATAL("Could not create afl state"); }
afl_state_init(afl);
afl_fsrv_init(&afl->fsrv);
read_afl_environment(afl, envp);
exit_1 = !!afl->afl_env.afl_bench_just_one;
SAYF(cCYA "afl-fuzz" VERSION cRST
" based on afl by Michal Zalewski and a big online community\n");
doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH;
gettimeofday(&tv, &tz);
afl->init_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
while ((opt = getopt(argc, argv,
"+c:i:I:o:f:m:t:T:dnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0)
switch (opt) {
case 'I': afl->infoexec = optarg; break;
case 'c': {
afl->shm.cmplog_mode = 1;
afl->cmplog_binary = ck_strdup(optarg);
break;
}
case 's': {
afl->init_seed = strtoul(optarg, 0L, 10);
afl->fixed_seed = 1;
break;
}
case 'p': /* Power schedule */
if (!stricmp(optarg, "fast")) {
afl->schedule = FAST;
} else if (!stricmp(optarg, "coe")) {
afl->schedule = COE;
} else if (!stricmp(optarg, "exploit")) {
afl->schedule = EXPLOIT;
} else if (!stricmp(optarg, "lin")) {
afl->schedule = LIN;
} else if (!stricmp(optarg, "quad")) {
afl->schedule = QUAD;
} else if (!stricmp(optarg, "mopt") || !stricmp(optarg, "mmopt")) {
afl->schedule = MMOPT;
} else if (!stricmp(optarg, "rare")) {
afl->schedule = RARE;
} else if (!stricmp(optarg, "explore") || !stricmp(optarg, "default") ||
!stricmp(optarg, "normal") || !stricmp(optarg, "afl")) {
afl->schedule = EXPLORE;
} else {
FATAL("Unknown -p power schedule");
}
break;
case 'e':
if (afl->file_extension) FATAL("Multiple -e options not supported");
afl->file_extension = optarg;
break;
case 'i': /* input dir */
if (afl->in_dir) FATAL("Multiple -i options not supported");
afl->in_dir = optarg;
if (!strcmp(afl->in_dir, "-")) afl->in_place_resume = 1;
break;
case 'o': /* output dir */
if (afl->out_dir) FATAL("Multiple -o options not supported");
afl->out_dir = optarg;
break;
case 'M': { /* master sync ID */
u8 *c;
if (afl->sync_id) FATAL("Multiple -S or -M options not supported");
afl->sync_id = ck_strdup(optarg);
if ((c = strchr(afl->sync_id, ':'))) {
*c = 0;
if (sscanf(c + 1, "%u/%u", &afl->master_id, &afl->master_max) != 2 ||
!afl->master_id || !afl->master_max ||
afl->master_id > afl->master_max || afl->master_max > 1000000)
FATAL("Bogus master ID passed to -M");
}
afl->force_deterministic = 1;
}
break;
case 'S':
if (afl->sync_id) FATAL("Multiple -S or -M options not supported");
afl->sync_id = ck_strdup(optarg);
break;
case 'f': /* target file */
if (afl->fsrv.out_file) FATAL("Multiple -f options not supported");
afl->fsrv.out_file = ck_strdup(optarg);
afl->fsrv.use_stdin = 0;
break;
case 'x': /* dictionary */
if (extras_dir) FATAL("Multiple -x options not supported");
extras_dir = optarg;
break;
case 't': { /* timeout */
u8 suffix = 0;
if (afl->timeout_given) FATAL("Multiple -t options not supported");
if (sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 ||
optarg[0] == '-')
FATAL("Bad syntax used for -t");
if (afl->fsrv.exec_tmout < 5) FATAL("Dangerously low value of -t");
if (suffix == '+')
afl->timeout_given = 2;
else
afl->timeout_given = 1;
break;
}
case 'm': { /* mem limit */
u8 suffix = 'M';
if (mem_limit_given) FATAL("Multiple -m options not supported");
mem_limit_given = 1;
if (!strcmp(optarg, "none")) {
afl->fsrv.mem_limit = 0;
break;
}
if (sscanf(optarg, "%llu%c", &afl->fsrv.mem_limit, &suffix) < 1 ||
optarg[0] == '-')
FATAL("Bad syntax used for -m");
switch (suffix) {
case 'T': afl->fsrv.mem_limit *= 1024 * 1024; break;
case 'G': afl->fsrv.mem_limit *= 1024; break;
case 'k': afl->fsrv.mem_limit /= 1024; break;
case 'M': break;
default: FATAL("Unsupported suffix or bad syntax for -m");
}
if (afl->fsrv.mem_limit < 5) FATAL("Dangerously low value of -m");
if (sizeof(rlim_t) == 4 && afl->fsrv.mem_limit > 2000)
FATAL("Value of -m out of range on 32-bit systems");
}
break;
case 'd': /* skip deterministic */
if (afl->skip_deterministic) FATAL("Multiple -d options not supported");
afl->skip_deterministic = 1;
afl->use_splicing = 1;
break;
case 'B': /* load bitmap */
/* This is a secret undocumented option! It is useful if you find
an interesting test case during a normal fuzzing process, and want
to mutate it without rediscovering any of the test cases already
found during an earlier run.
To use this mode, you need to point -B to the fuzz_bitmap produced
by an earlier run for the exact same binary... and that's it.
I only used this once or twice to get variants of a particular
file, so I'm not making this an official setting. */
if (afl->in_bitmap) FATAL("Multiple -B options not supported");
afl->in_bitmap = optarg;
read_bitmap(afl, afl->in_bitmap);
break;
case 'C': /* crash mode */
if (afl->crash_mode) FATAL("Multiple -C options not supported");
afl->crash_mode = FAULT_CRASH;
break;
case 'n': /* dumb mode */
if (afl->dumb_mode) FATAL("Multiple -n options not supported");
if (afl->afl_env.afl_dumb_forksrv)
afl->dumb_mode = 2;
else
afl->dumb_mode = 1;
break;
case 'T': /* banner */
if (afl->use_banner) FATAL("Multiple -T options not supported");
afl->use_banner = optarg;
break;
case 'Q': /* QEMU mode */
if (afl->qemu_mode) FATAL("Multiple -Q options not supported");
afl->qemu_mode = 1;
if (!mem_limit_given) afl->fsrv.mem_limit = MEM_LIMIT_QEMU;
break;
case 'N': /* Unicorn mode */
if (afl->no_unlink) FATAL("Multiple -N options not supported");
afl->no_unlink = 1;
break;
case 'U': /* Unicorn mode */
if (afl->unicorn_mode) FATAL("Multiple -U options not supported");
afl->unicorn_mode = 1;
if (!mem_limit_given) afl->fsrv.mem_limit = MEM_LIMIT_UNICORN;
break;
case 'W': /* Wine+QEMU mode */
if (afl->use_wine) FATAL("Multiple -W options not supported");
afl->qemu_mode = 1;
afl->use_wine = 1;
if (!mem_limit_given) afl->fsrv.mem_limit = 0;
break;
case 'V': {
afl->most_time_key = 1;
if (sscanf(optarg, "%llu", &afl->most_time) < 1 || optarg[0] == '-')
FATAL("Bad syntax used for -V");
} break;
case 'E': {
afl->most_execs_key = 1;
if (sscanf(optarg, "%llu", &afl->most_execs) < 1 || optarg[0] == '-')
FATAL("Bad syntax used for -E");
} break;
case 'L': { /* MOpt mode */
if (afl->limit_time_sig) FATAL("Multiple -L options not supported");
afl->limit_time_sig = 1;
afl->havoc_max_mult = HAVOC_MAX_MULT_MOPT;
if (sscanf(optarg, "%llu", &afl->limit_time_puppet) < 1 ||
optarg[0] == '-')
FATAL("Bad syntax used for -L");
u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000;
if (limit_time_puppet2 < afl->limit_time_puppet)
FATAL("limit_time overflow");
afl->limit_time_puppet = limit_time_puppet2;
SAYF("limit_time_puppet %llu\n", afl->limit_time_puppet);
afl->swarm_now = 0;
if (afl->limit_time_puppet == 0) afl->key_puppet = 1;
int i;
int tmp_swarm = 0;
if (afl->g_now > afl->g_max) afl->g_now = 0;
afl->w_now = (afl->w_init - afl->w_end) * (afl->g_max - afl->g_now) /
(afl->g_max) +
afl->w_end;
for (tmp_swarm = 0; tmp_swarm < swarm_num; ++tmp_swarm) {
double total_puppet_temp = 0.0;
afl->swarm_fitness[tmp_swarm] = 0.0;
for (i = 0; i < operator_num; ++i) {
afl->stage_finds_puppet[tmp_swarm][i] = 0;
afl->probability_now[tmp_swarm][i] = 0.0;
afl->x_now[tmp_swarm][i] =
((double)(random() % 7000) * 0.0001 + 0.1);
total_puppet_temp += afl->x_now[tmp_swarm][i];
afl->v_now[tmp_swarm][i] = 0.1;
afl->L_best[tmp_swarm][i] = 0.5;
afl->G_best[i] = 0.5;
afl->eff_best[tmp_swarm][i] = 0.0;
}
for (i = 0; i < operator_num; ++i) {
afl->stage_cycles_puppet_v2[tmp_swarm][i] =
afl->stage_cycles_puppet[tmp_swarm][i];
afl->stage_finds_puppet_v2[tmp_swarm][i] =
afl->stage_finds_puppet[tmp_swarm][i];
afl->x_now[tmp_swarm][i] =
afl->x_now[tmp_swarm][i] / total_puppet_temp;
}
double x_temp = 0.0;
for (i = 0; i < operator_num; ++i) {
afl->probability_now[tmp_swarm][i] = 0.0;
afl->v_now[tmp_swarm][i] =
afl->w_now * afl->v_now[tmp_swarm][i] +
RAND_C *
(afl->L_best[tmp_swarm][i] - afl->x_now[tmp_swarm][i]) +
RAND_C * (afl->G_best[i] - afl->x_now[tmp_swarm][i]);
afl->x_now[tmp_swarm][i] += afl->v_now[tmp_swarm][i];
if (afl->x_now[tmp_swarm][i] > v_max)
afl->x_now[tmp_swarm][i] = v_max;
else if (afl->x_now[tmp_swarm][i] < v_min)
afl->x_now[tmp_swarm][i] = v_min;
x_temp += afl->x_now[tmp_swarm][i];
}
for (i = 0; i < operator_num; ++i) {
afl->x_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i] / x_temp;
if (likely(i != 0))
afl->probability_now[tmp_swarm][i] =
afl->probability_now[tmp_swarm][i - 1] +
afl->x_now[tmp_swarm][i];
else
afl->probability_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i];
}
if (afl->probability_now[tmp_swarm][operator_num - 1] < 0.99 ||
afl->probability_now[tmp_swarm][operator_num - 1] > 1.01)
FATAL("ERROR probability");
}
for (i = 0; i < operator_num; ++i) {
afl->core_operator_finds_puppet[i] = 0;
afl->core_operator_finds_puppet_v2[i] = 0;
afl->core_operator_cycles_puppet[i] = 0;
afl->core_operator_cycles_puppet_v2[i] = 0;
afl->core_operator_cycles_puppet_v3[i] = 0;
}
} break;
case 'h': show_help++; break; // not needed
case 'R':
if (afl->use_radamsa)
afl->use_radamsa = 2;
else
afl->use_radamsa = 1;
break;
default:
if (!show_help) show_help = 1;
}
if (optind == argc || !afl->in_dir || !afl->out_dir || show_help)
usage(afl, argv[0], show_help);
OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" "
"Eißfeldt, Andrea Fioraldi and Dominik Maier");
OKF("afl++ is open source, get it at "
"https://github.com/AFLplusplus/AFLplusplus");
OKF("Power schedules from github.com/mboehme/aflfast");
OKF("Python Mutator and llvm_mode whitelisting from github.com/choller/afl");
OKF("afl-tmin fork server patch from github.com/nccgroup/TriforceAFL");
OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL");
if (afl->sync_id && afl->force_deterministic &&
getenv("AFL_CUSTOM_MUTATOR_ONLY"))
WARNF(
"Using -M master with the AFL_CUSTOM_MUTATOR_ONLY mutator options will "
"result in no deterministic mutations being done!");
if (afl->fixed_seed) OKF("Running with fixed seed: %u", (u32)afl->init_seed);
srandom((u32)afl->init_seed);
if (afl->use_radamsa) {
if (afl->limit_time_sig)
FATAL(
"MOpt and Radamsa are mutually exclusive. We accept pull requests "
"that integrates MOpt with the optional mutators "
"(custom/radamsa/redquenn/...).");
OKF("Using Radamsa add-on");
u8 * libradamsa_path = get_libradamsa_path(argv[0]);
void *handle = dlopen(libradamsa_path, RTLD_NOW);
ck_free(libradamsa_path);
if (!handle) FATAL("Failed to dlopen() libradamsa");
void (*radamsa_init_ptr)(void) = dlsym(handle, "radamsa_init");
afl->radamsa_mutate_ptr = dlsym(handle, "radamsa");
if (!radamsa_init_ptr || !afl->radamsa_mutate_ptr)
FATAL("Failed to dlsym() libradamsa");
/* randamsa_init installs some signal hadlers, call it before
setup_signal_handlers so that AFL++ can then replace those signal
handlers */
radamsa_init_ptr();
}
setup_signal_handlers();
check_asan_opts();
afl->power_name = power_names[afl->schedule];
if (afl->sync_id) fix_up_sync(afl);
if (!strcmp(afl->in_dir, afl->out_dir))
FATAL("Input and output directories can't be the same");
if (afl->dumb_mode) {
if (afl->crash_mode) FATAL("-C and -n are mutually exclusive");
if (afl->qemu_mode) FATAL("-Q and -n are mutually exclusive");
if (afl->unicorn_mode) FATAL("-U and -n are mutually exclusive");
}
if (get_afl_env("AFL_DISABLE_TRIM")) afl->disable_trim = 1;
if (getenv("AFL_NO_UI") && getenv("AFL_FORCE_UI"))
FATAL("AFL_NO_UI and AFL_FORCE_UI are mutually exclusive");
if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode)
WARNF(cLRD
"Target binary called without a prefixed path, make sure you are "
"fuzzing the right binary: " cRST "%s",
argv[optind]);
ACTF("Getting to work...");
switch (afl->schedule) {
case FAST: OKF("Using exponential power schedule (FAST)"); break;
case COE: OKF("Using cut-off exponential power schedule (COE)"); break;
case EXPLOIT:
OKF("Using exploitation-based constant power schedule (EXPLOIT)");
break;
case LIN: OKF("Using linear power schedule (LIN)"); break;
case QUAD: OKF("Using quadratic power schedule (QUAD)"); break;
case MMOPT: OKF("Using modified MOpt power schedule (MMOPT)"); break;
case RARE: OKF("Using rare edge focus power schedule (RARE)"); break;
case EXPLORE:
OKF("Using exploration-based constant power schedule (EXPLORE, default)");
break;
default: FATAL("Unknown power schedule"); break;
}
if (get_afl_env("AFL_NO_FORKSRV")) afl->no_forkserver = 1;
if (get_afl_env("AFL_NO_CPU_RED")) afl->no_cpu_meter_red = 1;
if (get_afl_env("AFL_NO_ARITH")) afl->no_arith = 1;
if (get_afl_env("AFL_SHUFFLE_QUEUE")) afl->shuffle_queue = 1;
if (get_afl_env("AFL_FAST_CAL")) afl->fast_cal = 1;
if (afl->afl_env.afl_autoresume) {
afl->autoresume = 1;
if (afl->in_place_resume) SAYF("AFL_AUTORESUME has no effect for '-i -'");
}
if (afl->afl_env.afl_hang_tmout) {
afl->hang_tmout = atoi(afl->afl_env.afl_hang_tmout);
if (!afl->hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
}
if (afl->dumb_mode == 2 && afl->no_forkserver)
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
afl->fsrv.use_fauxsrv = afl->dumb_mode == 1 || afl->no_forkserver;
if (getenv("LD_PRELOAD"))
WARNF(
"LD_PRELOAD is set, are you sure that is what to you want to do "
"instead of using AFL_PRELOAD?");
if (afl->afl_env.afl_preload) {
if (afl->qemu_mode) {
u8 *qemu_preload = getenv("QEMU_SET_ENV");
u8 *afl_preload = getenv("AFL_PRELOAD");
u8 *buf;
s32 i, afl_preload_size = strlen(afl_preload);
for (i = 0; i < afl_preload_size; ++i) {
if (afl_preload[i] == ',')
PFATAL(
"Comma (',') is not allowed in AFL_PRELOAD when -Q is "
"specified!");
}
if (qemu_preload)
buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
qemu_preload, afl_preload, afl_preload);
else
buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s",
afl_preload, afl_preload);
setenv("QEMU_SET_ENV", buf, 1);
ck_free(buf);
} else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
}
}
if (getenv("AFL_LD_PRELOAD"))
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
save_cmdline(afl, argc, argv);
fix_up_banner(afl, argv[optind]);
check_if_tty(afl);
if (afl->afl_env.afl_force_ui) afl->not_on_tty = 0;
if (get_afl_env("AFL_CAL_FAST")) {
/* Use less calibration cycles, for slow applications */
afl->cal_cycles = 3;
afl->cal_cycles_long = 5;
}
if (get_afl_env("AFL_DEBUG")) afl->debug = 1;
if (afl->afl_env.afl_custom_mutator_only) {
/* This ensures we don't proceed to havoc/splice */
afl->custom_only = 1;
/* Ensure we also skip all deterministic steps */
afl->skip_deterministic = 1;
}
get_core_count(afl);
#ifdef HAVE_AFFINITY
bind_to_free_cpu(afl);
#endif /* HAVE_AFFINITY */
check_crash_handling();
check_cpu_governor(afl);
afl->fsrv.trace_bits = afl_shm_init(&afl->shm, MAP_SIZE, afl->dumb_mode);
setup_post(afl);
if (!afl->in_bitmap) memset(afl->virgin_bits, 255, MAP_SIZE);
memset(afl->virgin_tmout, 255, MAP_SIZE);
memset(afl->virgin_crash, 255, MAP_SIZE);
init_count_class16();
setup_dirs_fds(afl);
setup_custom_mutator(afl);
setup_cmdline_file(afl, argv + optind);
read_testcases(afl);
load_auto(afl);
pivot_inputs(afl);
if (extras_dir) load_extras(afl, extras_dir);
if (!afl->timeout_given) find_timeout(afl);
if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL &&
!afl->in_place_resume) {
char tmpfile[afl->file_extension ? strlen(afl->tmp_dir) + 1 + 10 + 1 +
strlen(afl->file_extension) + 1
: strlen(afl->tmp_dir) + 1 + 10 + 1];
if (afl->file_extension) {
sprintf(tmpfile, "%s/.cur_input.%s", afl->tmp_dir, afl->file_extension);
} else {
sprintf(tmpfile, "%s/.cur_input", afl->tmp_dir);
}
if (access(tmpfile, F_OK) !=
-1) // there is still a race condition here, but well ...
FATAL(
"AFL_TMPDIR already has an existing temporary input file: %s - if "
"this is not from another instance, then just remove the file.",
tmpfile);
} else
afl->tmp_dir = afl->out_dir;
/* If we don't have a file name chosen yet, use a safe default. */
if (!afl->fsrv.out_file) {
u32 i = optind + 1;
while (argv[i]) {
u8 *aa_loc = strstr(argv[i], "@@");
if (aa_loc && !afl->fsrv.out_file) {
afl->fsrv.use_stdin = 0;
if (afl->file_extension) {
afl->fsrv.out_file = alloc_printf("%s/.cur_input.%s", afl->tmp_dir,
afl->file_extension);
} else {
afl->fsrv.out_file = alloc_printf("%s/.cur_input", afl->tmp_dir);
}
detect_file_args(argv + optind + 1, afl->fsrv.out_file,
&afl->fsrv.use_stdin);
break;
}
++i;
}
}
if (!afl->fsrv.out_file) setup_stdio_file(afl);
if (afl->cmplog_binary) {
if (afl->limit_time_sig)
FATAL(
"MOpt and CmpLog are mutually exclusive. We accept pull requests "
"that integrates MOpt with the optional mutators "
"(custom/radamsa/redquenn/...).");
if (afl->unicorn_mode)
FATAL("CmpLog and Unicorn mode are not compatible at the moment, sorry");
if (!afl->qemu_mode) check_binary(afl, afl->cmplog_binary);
}
check_binary(afl, argv[optind]);
afl->start_time = get_cur_time();
if (afl->qemu_mode) {
if (afl->use_wine)
use_argv = get_wine_argv(argv[0], &afl->fsrv.target_path, argc - optind,
argv + optind);
else
use_argv = get_qemu_argv(argv[0], &afl->fsrv.target_path, argc - optind,
argv + optind);
} else {
use_argv = argv + optind;
}
afl->argv = use_argv;
perform_dry_run(afl);
cull_queue(afl);
show_init_stats(afl);
seek_to = find_start_position(afl);
write_stats_file(afl, 0, 0, 0);
maybe_update_plot_file(afl, 0, 0);
save_auto(afl);
if (afl->stop_soon) goto stop_fuzzing;
/* Woop woop woop */
if (!afl->not_on_tty) {
sleep(4);
afl->start_time += 4000;
if (afl->stop_soon) goto stop_fuzzing;
}
// real start time, we reset, so this works correctly with -V
afl->start_time = get_cur_time();
while (1) {
u8 skipped_fuzz;
cull_queue(afl);
if (!afl->queue_cur) {
++afl->queue_cycle;
afl->current_entry = 0;
afl->cur_skipped_paths = 0;
afl->queue_cur = afl->queue;
while (seek_to) {
++afl->current_entry;
--seek_to;
afl->queue_cur = afl->queue_cur->next;
}
show_stats(afl);
if (afl->not_on_tty) {
ACTF("Entering queue cycle %llu.", afl->queue_cycle);
fflush(stdout);
}
/* If we had a full queue cycle with no new finds, try
recombination strategies next. */
if (afl->queued_paths == prev_queued) {
if (afl->use_splicing)
++afl->cycles_wo_finds;
else
afl->use_splicing = 1;
} else
afl->cycles_wo_finds = 0;
prev_queued = afl->queued_paths;
if (afl->sync_id && afl->queue_cycle == 1 &&
afl->afl_env.afl_import_first)
sync_fuzzers(afl);
}
skipped_fuzz = fuzz_one(afl);
if (!skipped_fuzz && !afl->stop_soon && afl->sync_id) {
if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(afl);
}
if (!afl->stop_soon && exit_1) afl->stop_soon = 2;
if (afl->stop_soon) break;
afl->queue_cur = afl->queue_cur->next;
++afl->current_entry;
if (afl->most_time_key == 1) {
u64 cur_ms_lv = get_cur_time();
if (afl->most_time * 1000 < cur_ms_lv - afl->start_time) {
afl->most_time_key = 2;
afl->stop_soon = 2;
break;
}
}
if (afl->most_execs_key == 1) {
if (afl->most_execs <= afl->total_execs) {
afl->most_execs_key = 2;
afl->stop_soon = 2;
break;
}
}
}
if (afl->queue_cur) show_stats(afl);
/*
* ATTENTION - the following 10 lines were copied from a PR to Google's afl
* repository - and slightly fixed.
* These lines have nothing to do with the purpose of original PR though.
* Looks like when an exit condition was completed (AFL_BENCH_JUST_ONE,
* AFL_EXIT_WHEN_DONE or AFL_BENCH_UNTIL_CRASH) the child and forkserver
* where not killed?
*/
/* if we stopped programmatically, we kill the forkserver and the current
runner. if we stopped manually, this is done by the signal handler */
if (afl->stop_soon == 2) {
if (afl->fsrv.child_pid > 0) kill(afl->fsrv.child_pid, SIGKILL);
if (afl->fsrv.fsrv_pid > 0) kill(afl->fsrv.fsrv_pid, SIGKILL);
if (afl->cmplog_child_pid > 0) kill(afl->cmplog_child_pid, SIGKILL);
if (afl->cmplog_fsrv_pid > 0) kill(afl->cmplog_fsrv_pid, SIGKILL);
/* Now that we've killed the forkserver, we wait for it to be able to get
* rusage stats. */
if (waitpid(afl->fsrv.fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); }
}
write_bitmap(afl);
write_stats_file(afl, 0, 0, 0);
maybe_update_plot_file(afl, 0, 0);
save_auto(afl);
stop_fuzzing:
afl->force_ui_update = 1; // ensure the screen is reprinted
show_stats(afl); // print the screen one last time
SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
afl->stop_soon == 2 ? "programmatically" : "by user");
if (afl->most_time_key == 2)
SAYF(cYEL "[!] " cRST "Time limit was reached\n");
if (afl->most_execs_key == 2)
SAYF(cYEL "[!] " cRST "Execution limit was reached\n");
/* Running for more than 30 minutes but still doing first cycle? */
if (afl->queue_cycle == 1 &&
get_cur_time() - afl->start_time > 30 * 60 * 1000) {
SAYF("\n" cYEL "[!] " cRST
"Stopped during the first cycle, results may be incomplete.\n"
" (For info on resuming, see %s/README.md)\n",
doc_path);
}
fclose(afl->fsrv.plot_file);
destroy_queue(afl);
destroy_extras(afl);
destroy_custom_mutator(afl);
afl_shm_deinit(&afl->shm);
afl_fsrv_deinit(&afl->fsrv);
if (afl->orig_cmdline) ck_free(afl->orig_cmdline);
ck_free(afl->fsrv.target_path);
ck_free(afl->fsrv.out_file);
ck_free(afl->sync_id);
free(afl); /* not tracked */
argv_cpy_free(argv);
alloc_report();
OKF("We're done here. Have a nice day!\n");
exit(0);
}
#endif /* !AFL_LIB */