You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AFLplusplus/afl-fuzz.c

3807 lines
123 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
american fuzzy lop++ - fuzzer code
--------------------------------
Originally written by Michal Zalewski
Now maintained by Marc Heuse <mh@mh-sec.de>,
Dominik Meier <mail@dmnk.co>,
Andrea Fioraldi <andreafioraldi@gmail.com>, and
Heiko Eissfeldt <heiko.eissfeldt@hexco.de>
Copyright 2016, 2017 Google Inc. All rights reserved.
Copyright 2019-2024 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:
https://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.
*/
//引入了 AFL++ 的核心头文件、CMPLog 相关头文件、通用函数头文件等。
//根据编译选项,可能还会包含其他系统头文件,如内存映射、文件操作等。
#include "afl-fuzz.h"
#include "cmplog.h"
#include "common.h"
#include <limits.h>
#include <stdlib.h>
#ifndef USEMMAP
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#ifdef HAVE_ZLIB
#define ck_gzread(fd, buf, len, fn) \
do { \
\
s32 _len = (s32)(len); \
s32 _res = gzread(fd, buf, _len); \
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
\
} while (0)
#define ck_gzwrite(fd, buf, len, fn) \
do { \
\
if (len <= 0) break; \
s32 _written = 0, _off = 0, _len = (s32)(len); \
\
do { \
\
s32 _res = gzwrite(fd, (buf) + _off, _len); \
if (_res != _len && (_res > 0 && _written + _res != _len)) { \
\
if (_res > 0) { \
\
_written += _res; \
_len -= _res; \
_off += _res; \
\
} else { \
\
RPFATAL(_res, "Short write to %s (%d of %d bytes)", fn, _res, \
_len); \
\
} \
\
} else { \
\
break; \
\
} \
\
} while (1); \
\
\
\
} while (0)
//定义了与 zlib 相关的宏,用于处理压缩文件的读写操作。
//对于苹果系统,引入了特定的头文件以支持线程优先级设置。
//如果启用了性能分析,则声明了一个外部变量 time_spent_working 用于记录工作时间
#include <zlib.h>
#define ZLIBOPEN gzopen
#define ZLIBREAD ck_gzread
#define NZLIBREAD gzread
#define ZLIBWRITE ck_gzwrite
#define ZLIBCLOSE gzclose
#define ZLIB_EXTRA "9"
#else
#define ZLIBOPEN open
#define NZLIBREAD read
#define ZLIBREAD ck_read
#define ZLIBWRITE ck_write
#define ZLIBCLOSE close
#endif
#ifdef __APPLE__
#include <sys/qos.h>
#include <pthread/qos.h>
#endif
#ifdef PROFILING
extern u64 time_spent_working;
#endif
//程序退出时的清理函数
static void at_exit() {//at_exit 函数在 AFL++ 程序退出时被调用,用于清理资源和终止子进程
//首先尝试获取环境变量 __AFL_TARGET_PID2 和 __AFL_TARGET_PID1这些变量存储了目标进程的 PID
s32 i, pid1 = 0, pid2 = 0, pgrp = -1;
char *list[4] = {SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, CMPLOG_SHM_ENV_VAR, NULL};
char *ptr;
//如果找到有效的 PID则获取其进程组 ID 并向进程组发送 SIGTERM 信号以终止整个进程组,
//然后单独向目标进程发送 SIGTERM 信号。
ptr = getenv("__AFL_TARGET_PID2");
if (ptr && *ptr && (pid2 = atoi(ptr)) > 0) {
pgrp = getpgid(pid2);
if (pgrp > 0) { killpg(pgrp, SIGTERM); }
kill(pid2, SIGTERM);
}
ptr = getenv("__AFL_TARGET_PID1");
if (ptr && *ptr && (pid1 = atoi(ptr)) > 0) {
pgrp = getpgid(pid1);
if (pgrp > 0) { killpg(pgrp, SIGTERM); }
kill(pid1, SIGTERM);
}
//尝试获取环境变量 CPU_AFFINITY_ENV_VAR如果存在且非空则删除该文件以解除 CPU 亲和性设置
ptr = getenv(CPU_AFFINITY_ENV_VAR);
if (ptr && *ptr) unlink(ptr);
//遍历 list 数组,获取每个环境变量的值,如果存在且非空,
//则根据编译选项使用不同的共享内存删除函数shm_unlink 或 shmctl来删除共享内存
i = 0;
while (list[i] != NULL) {
ptr = getenv(list[i]);
if (ptr && *ptr) {
#ifdef USEMMAP
shm_unlink(ptr);
#else
shmctl(atoi(ptr), IPC_RMID, NULL);
#endif
}
i++;
}
int kill_signal = SIGKILL;
/* AFL_KILL_SIGNAL should already be a valid int at this point */
if ((ptr = getenv("AFL_KILL_SIGNAL"))) { kill_signal = atoi(ptr); }
if (pid1 > 0) {
pgrp = getpgid(pid1);
if (pgrp > 0) { killpg(pgrp, kill_signal); }
kill(pid1, kill_signal);
}
if (pid2 > 0) {
pgrp = getpgid(pid1);
if (pgrp > 0) { killpg(pgrp, kill_signal); }
kill(pid2, kill_signal);
//最后,获取环境变量 AFL_KILL_SIGNAL 的值作为要发送的信号类型(默认为 SIGKILL
//然后再次检查 PID1 和 PID2获取其进程组 ID 并向进程组发送相应的信号,单独向目标进程发送信号以确保其被终止
}
}
/* Display usage hints. */
//主要为显示说明
static void usage(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 (or '-' to resume, "
"also see \n"
" AFL_AUTORESUME)\n"
" -o dir - output directory for fuzzer findings\n\n"
//-i dir指定输入目录该目录包含用于模糊测试的测试用例。如果输入为 -,则表示恢复之前的模糊测试会话。
//-o dir指定输出目录用于存储模糊测试过程中发现的结果。
"Execution control settings:\n"
" -P strategy - set fix mutation strategy: explore (focus on new "
"coverage),\n"
" exploit (focus on triggering crashes). You can also "
"set a\n"
" number of seconds after without any finds it switches "
"to\n"
" exploit mode, and back on new coverage (default: %u)\n"
" -p schedule - power schedules compute a seed's performance score:\n"
" explore(default), fast, exploit, seek, rare, mmopt, "
"coe, lin\n"
" quad -- see docs/FAQ.md for more information\n"
" -f file - location read by the fuzzed program (default: stdin "
"or @@)\n"
" -t msec - timeout for each run (auto-scaled, default %u ms). "
"Add a '+'\n"
" to auto-calculate the timeout, the value being the "
"maximum.\n"
" -m megs - memory limit for child process (%u MB, 0 = no limit "
"[default])\n"
//-P strategy设置固定的变异策略。可以选择 explore专注于发现新覆盖率或 exploit专注于触发崩溃。还可以设置在没有发现新结果时自动切换到 exploit 模式的时间(以秒为单位),并在发现新覆盖率时切换回 explore 模式。
//-p schedule设置功率调度策略用于计算种子的性能得分。可选的策略包括 explore默认、fast、exploit、seek、rare、mmopt、coe、lin 和 quad。具体的策略选择和效果可以参考 AFL++ 的文档。
//-f file指定被模糊测试程序读取的文件位置默认为标准输入或 @@。
//-t msec设置每次运行的超时时间自动缩放默认为指定的毫秒数。可以在超时值后加上 +,表示自动计算超时时间,指定的值为最大值。
//-m megs设置子进程的内存限制以兆字节为单位默认为 0表示不限制
#if defined(__linux__) && defined(__aarch64__)
" -A - use binary-only instrumentation (ARM CoreSight mode)\n"
#endif
" -O - use binary-only instrumentation (FRIDA mode)\n"
#if defined(__linux__)
" -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"
#endif
#if defined(__linux__)
" -X - use VM fuzzing (NYX mode - standalone mode)\n"
" -Y - use VM fuzzing (NYX mode - multiple instances mode)\n"
//-A在 ARM 架构的 Linux 系统上使用二进制插桩ARM CoreSight 模式)。
//-O使用 FRIDA 模式进行二进制插桩。
//-Q在 Linux 系统上,使用 QEMU 模式进行二进制插桩。
//-U在 Linux 系统上,使用基于 Unicorn 的插桩Unicorn 模式)。
//-W在 Linux 系统上,使用基于 QEMU 和 Wine 的插桩Wine 模式)。
//-X 和 -Y在 Linux 系统上,使用 VM 模糊测试NYX 模式),分别支持独立模式和多实例模式。
#endif
"\n"
"Mutator settings:\n"
" -a type - target input format, \"text\" or \"binary\" (default: "
"generic)\n"
" -g minlength - set min length of generated fuzz input (default: 1)\n"
" -G maxlength - set max length of generated fuzz input (default: "
"%lu)\n"
" -L minutes - use MOpt(imize) mode and set the time limit for "
"entering the\n"
" pacemaker mode (minutes of no new finds). 0 = "
"immediately,\n"
" -1 = immediately and together with normal mutation.\n"
" Note: this option is usually not very effective\n"
" -c program - enable CmpLog by specifying a binary compiled for "
"it.\n"
" if using QEMU/FRIDA or the fuzzing target is "
"compiled\n"
" for CmpLog then use '-c 0'. To disable CMPLOG use '-c "
"-'.\n"
" -l cmplog_opts - CmpLog configuration values (e.g. \"2ATR\"):\n"
" 1=small files, 2=larger files (default), 3=all "
"files,\n"
" A=arithmetic solving, T=transformational solving,\n"
" X=extreme transform solving, R=random colorization "
"bytes.\n\n"
"Fuzzing behavior settings:\n"
" -Z - sequential queue selection instead of weighted "
"random\n"
" -N - do not unlink the fuzzing input file (for devices "
"etc.)\n"
" -n - fuzz without instrumentation (non-instrumented mode)\n"
" -x dict_file - fuzzer dictionary (see README.md, specify up to 4 "
"times)\n\n"
"Test settings:\n"
" -s seed - use a fixed seed for the RNG\n"
" -V seconds - fuzz for a specified time then terminate (fuzz time "
"only!)\n"
" -E execs - fuzz for an approx. no. of total executions then "
"terminate\n"
" Note: not precise and can have several more "
"executions.\n\n"
"Other stuff:\n"
" -M/-S id - distributed mode (-M sets -Z and disables trimming)\n"
" see docs/fuzzing_in_depth.md#c-using-multiple-cores\n"
" for effective recommendations for parallel fuzzing.\n"
" -F path - sync to a foreign fuzzer queue directory (requires "
"-M, can\n"
" be specified up to %u times)\n"
" -z - skip the enhanced deterministic fuzzing\n"
" (note that the old -d and -D flags are ignored.)\n"
" -T text - text banner to show on the screen\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/default/fuzz_bitmap file\n"
" -C - crash exploration mode (the peruvian rabbit thing)\n"
" -b cpu_id - bind the fuzzing process to the specified CPU core "
"(0-...)\n"
" -e ext - file extension for the fuzz test input file (if "
"needed)\n"
"\n",
argv0, STRATEGY_SWITCH_TIME, EXEC_TIMEOUT, MEM_LIMIT, MAX_FILE,
FOREIGN_SYNCS_MAX);
if (more_help > 1) {
#if defined USE_COLOR && !defined ALWAYS_COLORED
#define DYN_COLOR \
"AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n"
//定义了 USE_COLOR 但未定义 ALWAYS_COLORED则定义 DYN_COLOR 宏,用于提示用户如何关闭控制台的颜色输出
#else
#define DYN_COLOR
#endif
//如果定义了 AFL_PERSISTENT_RECORD则定义 PERSISTENT_MSG 宏,用于提示用户 AFL_PERSISTENT_RECORD 环境变量的作用
#ifdef AFL_PERSISTENT_RECORD
#define PERSISTENT_MSG \
"AFL_PERSISTENT_RECORD: record the last X inputs to every crash in " \
"out/crashes\n"
#else
#define PERSISTENT_MSG
#endif
SAYF(//用于输出详细的帮助信息,包括 AFL++ 使用的各种环境变量及其作用。
"Environment variables used:\n"
"LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\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_AUTORESUME: resume fuzzing if directory specified by -o already exists\n"
"AFL_BENCH_JUST_ONE: run the target just once\n"
"AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n"
"AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)\n"
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\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_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n"
"AFL_DEBUG: extra debugging output for Python mode trimming\n"
"AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n"
"AFL_DISABLE_REDUNDANT: disable any queue item that is redundant\n"
"AFL_DISABLE_TRIM: disable the trimming of test cases\n"
"AFL_DUMB_FORKSRV: use fork server without feedback from target\n"
"AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n"
"AFL_EXIT_ON_TIME: exit when no new coverage is found within the specified time\n"
"AFL_EXIT_ON_SEED_ISSUES: exit on any kind of seed issues\n"
"AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60\n"
" minutes and a cycle without finds)\n"
"AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n"
"AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n"
"AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in ms)\n"
"AFL_HANG_TMOUT: override timeout value (in milliseconds)\n"
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n"
"AFL_IGNORE_PROBLEMS: do not abort fuzzing if an incorrect setup is detected\n"
"AFL_IGNORE_PROBLEMS_COVERAGE: if set in addition to AFL_IGNORE_PROBLEMS - also\n"
" ignore those libs for coverage\n"
"AFL_IGNORE_SEED_PROBLEMS: skip over crashes and timeouts in the seeds instead of\n"
" exiting\n"
"AFL_IGNORE_TIMEOUTS: do not process or save any timeouts\n"
"AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n"
"AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n"
"AFL_INPUT_LEN_MIN/AFL_INPUT_LEN_MAX: like -g/-G set min/max fuzz length produced\n"
"AFL_PIZZA_MODE: 1 - enforce pizza mode, -1 - disable for April 1st,\n"
" 0 (default) - activate on April 1st\n"
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n"
" (default: SIGKILL)\n"
"AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n"
" (default: SIGTERM). If unset and AFL_KILL_SIGNAL is\n"
" set, that value will be used.\n"
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
" the target was compiled for\n"
"AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n"
" then they are randomly selected instead all of them being\n"
" used. Defaults to 200.\n"
"AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n"
"AFL_TRY_AFFINITY: try to bind to an unused core, but don't fail if unsuccessful\n"
"AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n"
"AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target\n"
"AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n"
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
"AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n"
"AFL_NO_STARTUP_CALIBRATION: no initial seed calibration, start fuzzing at once\n"
"AFL_NO_WARN_INSTABILITY: no warn about instability issues on startup calibration\n"
"AFL_NO_UI: switch status screen off\n"
"AFL_NYX_AUX_SIZE: size of the Nyx auxiliary buffer. Must be a multiple of 4096.\n"
" Increase this value in case the crash reports are truncated.\n"
" Default value is 4096.\n"
"AFL_NYX_DISABLE_SNAPSHOT_MODE: disable snapshot mode (must be supported by the agent)\n"
"AFL_NYX_LOG: output NYX hprintf messages to another file\n"
"AFL_NYX_REUSE_SNAPSHOT: reuse an existing Nyx root snapshot\n"
DYN_COLOR
"AFL_PATH: path to AFL support binaries\n"
"AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n"
"AFL_QUIET: suppress forkserver status messages\n"
PERSISTENT_MSG
"AFL_POST_PROCESS_KEEP_ORIGINAL: save the file as it was prior post-processing to\n"
" the queue, but execute the post-processed one\n"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
"AFL_TARGET_ENV: pass extra environment variables to target\n"
"AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n"
"AFL_SKIP_BIN_CHECK: skip afl compatibility checks, also disables auto map size\n"
"AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n"
//"AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n"
"AFL_STATSD: enables StatsD metrics collection\n"
"AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n"
"AFL_STATSD_PORT: change default statsd port (default: 8125)\n"
"AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n"
" suported formats: dogstatsd, librato, signalfx, influxdb\n"
"AFL_NO_FASTRESUME: do not read or write a fast resume file\n"
"AFL_NO_SYNC: disables all syncing\n"
"AFL_SYNC_TIME: sync time between fuzzing instances (in minutes)\n"
"AFL_FINAL_SYNC: sync a final time when exiting (will delay the exit!)\n"
"AFL_NO_CRASH_README: do not create a README in the crashes directory\n"
"AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n"
"AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n"
"AFL_EARLY_FORKSERVER: force an early forkserver in an afl-clang-fast/\n"
" afl-clang-lto/afl-gcc-fast target\n"
"AFL_PERSISTENT: enforce persistent mode (if __AFL_LOOP is in a shared lib)\n"
"AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a shared lib)\n"
"AFL_FUZZER_STATS_UPDATE_INTERVAL: interval to update fuzzer_stats file in\n"
" seconds (default: 60, minimum: 1)\n"
"\n"
);
/*
LD_BIND_LAZY指示 AFL++ 不要为目标程序设置 LD_BIND_NOW 环境变量。
ASAN_OPTIONS 和 MSAN_OPTIONS用于自定义 ASAN 和 MSAN 的设置,必须包含特定的选项以确保 AFL++ 正常工作。
AFL_AUTORESUME如果输出目录已存在则自动恢复模糊测试。
AFL_BENCH_JUST_ONE 和 AFL_BENCH_UNTIL_CRASH用于性能测试分别表示只运行目标程序一次和在找到第一个崩溃输入后退出。
AFL_CMPLOG_ONLY_NEW在初始测试用例上不运行 CMPLog适用于恢复模糊测试。
AFL_CRASH_EXITCODE指定 AFL++ 应解释为崩溃的子进程退出代码。
AFL_CUSTOM_MUTATOR_LIBRARY 和 AFL_CUSTOM_MUTATOR_ONLY用于自定义变异器库和仅使用自定义变异器。
AFL_CYCLE_SCHEDULES在完成一个周期后切换到不同的功率调度策略。
AFL_DEBUG 和 AFL_DEBUG_CHILD用于调试输出和不抑制目标程序的 stdout/stderr。
AFL_DISABLE_REDUNDANT 和 AFL_DISABLE_TRIM禁用冗余队列项和测试用例的修剪。
AFL_DUMB_FORKSRV使用没有目标反馈的 fork 服务器。
AFL_EXIT_WHEN_DONE 和 AFL_EXIT_ON_TIME在所有输入运行完毕且没有新发现时退出或在指定时间内没有发现新覆盖率时退出。
AFL_EXIT_ON_SEED_ISSUES在种子问题时退出。
AFL_EXPAND_HAVOC_NOW立即启用扩展的 Havoc 模式。
AFL_FAST_CAL限制校准阶段为三个周期以加速。
AFL_FORCE_UI强制显示状态屏幕适用于虚拟控制台
AFL_FORKSRV_INIT_TMOUT在启动时等待 fork 服务器的时间(以毫秒为单位)。
AFL_HANG_TMOUT覆盖超时值以毫秒为单位
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES不警告关于核心转储处理器的问题。
AFL_IGNORE_PROBLEMS 和 AFL_IGNORE_PROBLEMS_COVERAGE不因检测到不正确的设置而终止模糊测试以及忽略相关库的覆盖率。
AFL_IGNORE_SEED_PROBLEMS 和 AFL_IGNORE_TIMEOUTS在种子中跳过崩溃和超时而不是退出。
AFL_IGNORE_UNKNOWN_ENVS不警告未知的环境变量。
AFL_IMPORT_FIRST首先同步并导入来自其他模糊测试实例的测试用例。
AFL_INPUT_LEN_MIN/AFL_INPUT_LEN_MAX设置 AFL++ 产生的模糊长度的最小值和最大值。
AFL_PIZZA_MODE控制 AFL++ 是否在 4 月 1 日启用“披萨模式”。
AFL_KILL_SIGNAL 和 AFL_FORK_SERVER_KILL_SIGNAL指定在超时等情况下发送给子进程和 fork 服务器的信号 ID。
AFL_MAP_SIZE目标程序的共享内存大小必须大于或等于目标程序编译时的大小。
AFL_MAX_DET_EXTRAS如果字典列表中的条目多于该值则随机选择而不是使用所有条目。
AFL_NO_AFFINITY 和 AFL_TRY_AFFINITY不检查未使用的 CPU 核心用于模糊测试,或尝试绑定到未使用的 CPU 核心但不失败。
AFL_NO_ARITH 和 AFL_NO_AUTODICT在确定性阶段跳过算术变异和不加载目标程序中编译的自动字典。
AFL_NO_CPU_RED 和 AFL_NO_FORKSRV避免显示非常高的 CPU 使用率的红色和通过 execve 运行目标程序而不是使用 fork 服务器。
AFL_NO_SNAPSHOT 和 AFL_NO_STARTUP_CALIBRATION不使用快照功能和不进行初始种子校准。
AFL_NO_WARN_INSTABILITY 和 AFL_NO_UI不在启动校准期间警告稳定性问题和关闭状态屏幕。
AFL_NYX_AUX_SIZENyx 辅助缓冲区的大小,必须是 4096 的倍数。
AFL_NYX_DISABLE_SNAPSHOT_MODE 和 AFL_NYX_LOG禁用快照模式和将 NYX hprintf 消息输出到另一个文件。
AFL_NYX_REUSE_SNAPSHOT重用现有的 Nyx 根快照。
AFL_PATHAFL 支持二进制文件的路径。
AFL_PYTHON_MODULE使用指定的 Python 模块变异和修剪输入。
AFL_QUIET抑制 fork 服务器状态消息。
AFL_POST_PROCESS_KEEP_ORIGINAL保存后处理前的文件到队列中但执行后处理后的文件。
AFL_PRELOAD目标程序的 LD_PRELOAD/DYLD_INSERT_LIBRARIES 设置。
AFL_TARGET_ENV传递额外的环境变量给目标程序。
AFL_SHUFFLE_QUEUE在启动时随机重新排序输入队列。
AFL_SKIP_BIN_CHECK 和 AFL_SKIP_CPUFREQ跳过 AFL 兼容性检查和不警告 CPU 时钟变化。
AFL_STATSD 和相关变量:启用 StatsD 指标收集及其配置。
AFL_NO_FASTRESUME 和 AFL_NO_SYNC不读写快速恢复文件和禁用所有同步。
AFL_SYNC_TIME 和 AFL_FINAL_SYNC模糊测试实例之间的同步时间和退出时的最终同步。
AFL_NO_CRASH_README不在崩溃目录中创建 README 文件。
AFL_TESTCACHE_SIZE 和 AFL_TMPDIR测试用例缓存大小和输入文件生成目录。
AFL_EARLY_FORKSERVER 和 AFL_PERSISTENT在 AFL-clang-fast/AFL-clang-lto/AFL-gcc-fast 目标中强制早期 fork 服务器和持久模式。
AFL_DEFER_FORKSRV强制延迟 fork 服务器__AFL_INIT 在共享库中)。
AFL_FUZZER_STATS_UPDATE_INTERVAL更新 fuzzer_stats 文件的间隔(以秒为单位)。*/
} 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_mutators.md\n",
(char *)PYTHON_VERSION);
/*
如果定义了 USE_PYTHON则输出 AFL++ 是使用 Python 模块支持编译的,并提示用户查看相关文档以了解如何使用自定义变异器。
否则,输出 AFL++ 是没有 Python 模块支持编译的*/
#else
SAYF("Compiled without Python module support.\n");
#endif
/*如果定义了 AFL_PERSISTENT_RECORD则输出 AFL++ 是使用持久记录支持编译的。
否则,输出 AFL++ 是没有持久记录支持编译的。*/
#ifdef AFL_PERSISTENT_RECORD
SAYF("Compiled with AFL_PERSISTENT_RECORD support.\n");
#else
SAYF("Compiled without AFL_PERSISTENT_RECORD support.\n");
#endif
/*如果定义了 USEMMAP则输出 AFL++ 是使用 shm_open 支持编译的。
否则,输出 AFL++ 是使用 shmat 支持编译的。*/
#ifdef USEMMAP
SAYF("Compiled with shm_open support.\n");
#else
SAYF("Compiled with shmat support.\n");
#endif
/*如果定义了 ASAN_BUILD则输出 AFL++ 是使用 ASAN 构建编译的。*/
#ifdef ASAN_BUILD
SAYF("Compiled with ASAN_BUILD.\n");
#endif
/*如果定义了 NO_SPLICING则输出 AFL++ 是使用禁止拼接选项编译的*/
#ifdef NO_SPLICING
SAYF("Compiled with NO_SPLICING.\n");
#endif
/*如果定义了 FANCY_BOXES_NO_UTF则输出 AFL++ 是没有 UTF-8 支持编译的,这会影响状态屏幕中的线条渲染。*/
#ifdef FANCY_BOXES_NO_UTF
SAYF("Compiled without UTF-8 support for line rendering in status screen.\n");
#endif
/*如果定义了 PROFILING则输出 AFL++ 是使用性能分析编译的。*/
#ifdef PROFILING
SAYF("Compiled with PROFILING.\n");
#endif
/*如果定义了 INTROSPECTION则输出 AFL++ 是使用自省编译的。*/
#ifdef INTROSPECTION
SAYF("Compiled with INTROSPECTION.\n");
#endif
/*如果定义了 _DEBUG则输出 AFL++ 是使用调试模式编译的*/
#ifdef _DEBUG
SAYF("Compiled with _DEBUG.\n");
#endif
/*如果定义了 _AFL_DOCUMENT_MUTATIONS则输出 AFL++ 是使用记录变异编译的。*/
#ifdef _AFL_DOCUMENT_MUTATIONS
SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS.\n");
#endif
/*如果定义了 _AFL_SPECIAL_PERFORMANCE则输出 AFL++ 是使用特定系统的特殊性能选项编译的,并提醒用户这可能不适用于其他平台。*/
#ifdef _AFL_SPECIAL_PERFORMANCE
SAYF(
"Compiled with special performance options for this specific system, it "
"might not work on other platforms!\n");
#endif
SAYF("For additional help please consult %s/README.md :)\n\n", doc_path);
exit(1);
#undef PHYTON_SUPPORT
/*额外帮助:输出提示信息,建议用户查阅 README.md 文件以获取更多帮助。
退出程序:调用 exit(1) 退出程序,返回状态码 1 表示程序因错误或异常而退出。
取消宏定义:使用 #undef PHYTON_SUPPORT 取消 PHYTON_SUPPORT 宏的定义,尽管这里可能是一个拼写错误,应该是 PYTHON_SUPPORT。*/
}
#ifndef AFL_LIB
static int stricmp(char const *a, char const *b) {
if (!a || !b) { FATAL("Null reference"); }
for (;; ++a, ++b) {
int d;
d = tolower((int)*a) - tolower((int)*b);
if (d != 0 || !*a) { return d; }
}
}
static void fasan_check_afl_preload(char *afl_preload) {
char first_preload[PATH_MAX + 1] = {0};
char *separator = strchr(afl_preload, ':');
size_t first_preload_len = PATH_MAX;
char *basename;
char clang_runtime_prefix[] = "libclang_rt.asan";
if (separator != NULL && (separator - afl_preload) < PATH_MAX) {
first_preload_len = separator - afl_preload;
}
strncpy(first_preload, afl_preload, first_preload_len);
basename = strrchr(first_preload, '/');
if (basename == NULL) {
basename = first_preload;
} else {
basename = basename + 1;
}
if (strncmp(basename, clang_runtime_prefix,
sizeof(clang_runtime_prefix) - 1) != 0) {
FATAL("Address Sanitizer DSO must be the first DSO in AFL_PRELOAD");
}
if (access(first_preload, R_OK) != 0) {
FATAL("Address Sanitizer DSO not found");
}
OKF("Found ASAN DSO: %s", first_preload);
}
/* Main entry point */
int main(int argc, char **argv_orig, char **envp) {
s32 opt, auto_sync = 0 /*, user_set_cache = 0*/;//用于存储 getopt 函数返回的选项字符
u64 prev_queued = 0;//自动同步标志,初始值为 0。
//用于记录上一次排队的项目数量。
u32 sync_interval_cnt /*同步间隔计数器*/= 0, seek_to = 0, show_help = 0, default_output = 1,
map_size = get_map_size();
u8 *extras_dir[4];
u8 mem_limit_given = 0, exit_1 = 0, debug = 0,
extras_dir_cnt = 0 /*, have_p = 0*/;
char *afl_preload;
char *frida_afl_preload = NULL;
char **use_argv;
struct timeval tv;
struct timezone tz;
/*eek_to用于指定从哪个位置开始处理队列。
show_help显示帮助信息的标志。
default_output默认输出标志初始值为 1。
map_size共享内存映射的大小通过 get_map_size 函数获取。
extras_dir[4]:存储额外字典目录的数组。
mem_limit_given内存限制是否已指定的标志。
exit_1是否仅运行一次的标志。
debug调试模式标志。
extras_dir_cnt额外字典目录的数量。
afl_preload 和 frida_afl_preload用于存储 AFL 预加载库的路径。
use_argv用于存储处理后的命令行参数。
struct timeval tv 和 struct timezone tz用于获取当前时间*/
doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH;
//根据 DOC_PATH 环境变量或默认路径 "docs" 初始化文档路径
if (argc > 1 && strcmp(argv_orig[1], "--version") == 0) {
printf("afl-fuzz" VERSION "\n");
exit(0);
}
if (argc > 1 && strcmp(argv_orig[1], "--help") == 0) {
usage(argv_orig[0], 1);
exit(0);
}
/*版本信息:如果命令行参数包含 --version则输出 AFL++ 的版本信息并退出程序。
帮助信息:如果命令行参数包含 --help则调用 usage 函数显示帮助信息并退出程序。*/
#if defined USE_COLOR && defined ALWAYS_COLORED
if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) {
WARNF(
"Setting AFL_NO_COLOR has no effect (colors are configured on at "
"compile time)");
}
/*颜色输出:如果定义了 USE_COLOR 和 ALWAYS_COLORED并且环境变量 AFL_NO_COLOR 或 AFL_NO_COLOUR 被设置,则输出警告信息,提示用户颜色设置在编译时已配置。*/
#endif
char **argv = argv_cpy_dup(argc, argv_orig);
//argv_cpy_dup复制命令行参数数组以便在后续处理中使用
afl_state_t *afl = calloc(1, sizeof(afl_state_t));
if (!afl) { FATAL("Could not create afl state"); }
if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; }
afl_state_init(afl, map_size);
afl->debug = debug;
afl_fsrv_init(&afl->fsrv);
if (debug) { afl->fsrv.debug = true; }
read_afl_environment(afl, envp);
if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; }
exit_1 = !!afl->afl_env.afl_bench_just_one;
//SAYF输出 AFL++ 的版本信息和基于的原始 AFL 作者信息。
SAYF(cCYA "afl-fuzz" VERSION cRST
" based on afl by Michal Zalewski and a large online community\n");
//gettimeofday获取当前时间。
//rand_set_seed根据当前时间设置随机种子用于后续的随机数生成
gettimeofday(&tv, &tz);
rand_set_seed(afl, tv.tv_sec ^ tv.tv_usec ^ getpid());
//afl->shmem_testcase_mode设置为 1表示始终尝试使用共享内存进行模糊测试。
afl->shmem_testcase_mode = 1; // we always try to perform shmem fuzzing
/*分配内存:为 afl_state_t 结构体分配内存,用于存储 AFL++ 的状态信息。
调试模式:检查环境变量 AFL_DEBUG如果设置则启用调试模式。
初始化状态:调用 afl_state_init 函数初始化 AFL 状态,设置共享内存映射大小。
文件服务器初始化:调用 afl_fsrv_init 函数初始化文件服务器状态。
读取环境变量:调用 read_afl_environment 函数读取 AFL++ 相关的环境变量设置。
内存映射大小:如果环境变量中指定了共享内存映射大小,则更新文件服务器状态中的映射大小。
仅运行一次:检查是否仅运行一次的标志*/
// still available: HjJkKqruvwz
while ((opt = getopt(argc, argv,
"+a:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:"
"T:UV:WXx:YzZ")) > 0) {
switch (opt) {
case 'a':
if (!stricmp(optarg, "text") || !stricmp(optarg, "ascii") ||
!stricmp(optarg, "txt") || !stricmp(optarg, "asc")) {
afl->input_mode = 1;
} else if (!stricmp(optarg, "bin") || !stricmp(optarg, "binary")) {
afl->input_mode = 2;
} else if (!stricmp(optarg, "def") || !stricmp(optarg, "default")) {
afl->input_mode = 0;
} else {
FATAL("-a input mode needs to be \"text\" or \"binary\".");
}
break;
case 'P':
if (!stricmp(optarg, "explore") || !stricmp(optarg, "exploration")) {
afl->fuzz_mode = 0;
afl->switch_fuzz_mode = 0;
} else if (!stricmp(optarg, "exploit") ||
!stricmp(optarg, "exploitation")) {
afl->fuzz_mode = 1;
afl->switch_fuzz_mode = 0;
} else {
if ((afl->switch_fuzz_mode = (u32)atoi(optarg)) > INT_MAX) {
FATAL(
"Parameter for option -P must be \"explore\", \"exploit\" or a "
"number!");
} else {
afl->switch_fuzz_mode *= 1000;
}
}
break;
case 'g':
afl->min_length = atoi(optarg);
break;
case 'G':
afl->max_length = atoi(optarg);
break;
case 'Z':
afl->old_seed_selection = 1;
break;
case 'I':
afl->infoexec = optarg;
break;
case 'b': { /* bind CPU core */
if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported");
if (sscanf(optarg, "%d", &afl->cpu_to_bind) < 0) {
FATAL("Bad syntax used for -b");
}
/*getopt 循环:使用 getopt 函数解析命令行选项。
-a 选项:设置输入模式,可以是 "text"、"binary" 或 "default"。
-P 选项:设置固定的变异策略,可以是 "explore"、"exploit" 或指定一个秒数,表示在没有发现新结果时自动切换到 "exploit" 模式的时间。
-g 选项:设置生成的模糊输入的最小长度。
-G 选项:设置生成的模糊输入的最大长度。
-Z 选项:启用旧的种子选择方式。
-I 选项:设置信息执行命令。
-b 选项:绑定 AFL++ 进程到指定的 CPU 核心*/
break;
}
case 'c': {
/*-c 选项
功能:用于启用或禁用 CMPLog 模式。
逻辑:
如果选项参数为 "-",则表示禁用 CMPLog 模式。如果之前已经启用,则输出提示信息并禁用 CMPLog 模式,同时将 cmplog_binary 设置为 NULL。
如果选项参数不为 "-",则启用 CMPLog 模式,并将 cmplog_binary 设置为选项参数指定的路径。*/
if (strcmp(optarg, "-") == 0) {
if (afl->shm.cmplog_mode) {
ACTF("Disabling cmplog again because of '-c -'.");
afl->shm.cmplog_mode = 0;
afl->cmplog_binary = NULL;
}
} else {
afl->shm.cmplog_mode = 1;
afl->cmplog_binary = ck_strdup(optarg);
}
break;
}
case 's': {
/*-s 选项
功能:用于设置固定的随机种子。
逻辑:
如果选项参数为 NULL则输出错误信息并终止程序。
否则,使用选项参数指定的种子值初始化随机数生成器,并设置 fixed_seed 标志为 1表示使用固定的随机种子。*/
if (optarg == NULL) { FATAL("No valid seed provided. Got NULL."); }
rand_set_seed(afl, strtoul(optarg, 0L, 10));
afl->fixed_seed = 1;
break;
}
case 'p': /* Power schedule */
/*-p 选项
功能:用于设置功率调度策略,决定如何计算种子的性能得分。
逻辑:
根据选项参数的值,将 afl->schedule 设置为相应的功率调度策略枚举值。支持的策略包括 FAST、COE、EXPLOIT、LIN、QUAD、MMOPT、RARE、EXPLORE 和 SEEK。
如果选项参数不匹配任何已知策略,则输出错误信息并终止程序。
*/
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, "afl") ||
!stricmp(optarg, "default") ||
!stricmp(optarg, "normal")) {
afl->schedule = EXPLORE;
} else if (!stricmp(optarg, "seek")) {
afl->schedule = SEEK;
} else {
FATAL("Unknown -p power schedule");
}
// have_p = 1;
break;
case 'e':
/*-e 选项
功能:用于设置目标程序输入文件的扩展名。
逻辑:
如果 afl->file_extension 已经被设置,则输出错误信息并终止程序,表示不支持多个 -e 选项。
否则,将 afl->file_extension 设置为选项参数指定的扩展名。*/
if (afl->file_extension) { FATAL("Multiple -e options not supported"); }
afl->file_extension = optarg;
break;
case 'i': /* input dir */
/*-i 选项
功能:用于指定输入目录,该目录包含用于模糊测试的测试用例。
逻辑:
如果 afl->in_dir 已经被设置,则输出错误信息并终止程序,表示不支持多个 -i 选项。
如果选项参数为 NULL则输出错误信息并终止程序表示无效的 -i 选项。
否则,将 afl->in_dir 设置为选项参数指定的输入目录路径。
如果输入目录为 "-",则设置 afl->in_place_resume 标志为 1表示恢复之前的模糊测试会话。*/
if (afl->in_dir) { FATAL("Multiple -i options not supported"); }
if (optarg == NULL) { FATAL("Invalid -i option (got NULL)."); }
afl->in_dir = optarg;
if (!strcmp(afl->in_dir, "-")) { afl->in_place_resume = 1; }
break;
case 'o': /* output dir */
/*-o 选项
功能:用于指定输出目录,用于存储模糊测试过程中发现的结果。
逻辑:
如果 afl->out_dir 已经被设置,则输出错误信息并终止程序,表示不支持多个 -o 选项。
否则,将 afl->out_dir 设置为选项参数指定的输出目录路径。*/
if (afl->out_dir) { FATAL("Multiple -o options not supported"); }
afl->out_dir = optarg;
break;
case 'M': { /* main sync ID */
/*-M 选项
功能:用于设置主同步 ID用于分布式模糊测试中的主节点。
逻辑:
检查是否处于非插桩模式或 ARM CoreSight 模式,如果是,则输出错误信息并终止程序,因为这些模式不支持 -M 选项。
检查是否已经设置了同步 ID如果是则输出错误信息并终止程序表示不支持多个 -S 或 -M 选项。
检查选项参数是否以 - 开头,如果是,则输出错误信息并终止程序,因为 - 通常用于选项。
将同步 ID 设置为选项参数指定的值。
设置 old_seed_selection 标志为 1表示使用旧的队列遍历种子选择方式。
设置 disable_trim 标志为 1表示禁用修剪功能。
如果同步 ID 中包含 :,则解析主节点 ID 和主节点最大值,并进行有效性检查。如果解析失败或值无效,则输出错误信息并终止程序。
设置 is_main_node 标志为 1表示当前节点是主节点。
*/
u8 *c;
if (afl->non_instrumented_mode) {
FATAL("-M is not supported in non-instrumented mode");
}
if (afl->fsrv.cs_mode) {
FATAL("-M is not supported in ARM CoreSight mode");
}
if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); }
/* sanity check for argument: should not begin with '-' (possible
* option) */
if (optarg && *optarg == '-') {
FATAL(
"argument for -M started with a dash '-', which is used for "
"options");
}
afl->sync_id = ck_strdup(optarg);
afl->old_seed_selection = 1; // force old queue walking seed selection
afl->disable_trim = 1; // disable trimming
if ((c = strchr(afl->sync_id, ':'))) {
*c = 0;
if (sscanf(c + 1, "%u/%u", &afl->main_node_id, &afl->main_node_max) !=
2 ||
!afl->main_node_id || !afl->main_node_max ||
afl->main_node_id > afl->main_node_max ||
afl->main_node_max > 1000000) {
FATAL("Bogus main node ID passed to -M");
}
}
afl->is_main_node = 1;
}
break;
case 'S': /* secondary sync id */
/*-S 选项
功能:用于设置次级同步 ID用于分布式模糊测试中的次级节点。
逻辑:
检查是否处于非插桩模式或 ARM CoreSight 模式,如果是,则输出错误信息并终止程序,因为这些模式不支持 -S 选项。
检查是否已经设置了同步 ID如果是则输出错误信息并终止程序表示不支持多个 -S 或 -M 选项。
检查选项参数是否以 - 开头,如果是,则输出错误信息并终止程序,因为 - 通常用于选项。
将同步 ID 设置为选项参数指定的值。
设置 is_secondary_node 标志为 1表示当前节点是次级节点*/
if (afl->non_instrumented_mode) {
FATAL("-S is not supported in non-instrumented mode");
}
if (afl->fsrv.cs_mode) {
FATAL("-S is not supported in ARM CoreSight mode");
}
if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); }
/* sanity check for argument: should not begin with '-' (possible
* option) */
if (optarg && *optarg == '-') {
FATAL(
"argument for -M started with a dash '-', which is used for "
"options");
}
afl->sync_id = ck_strdup(optarg);
afl->is_secondary_node = 1;
break;
case 'F': /* foreign sync dir */
/*-F 选项
功能:用于指定外部同步目录,用于分布式模糊测试中与其他模糊测试实例同步。
逻辑:
检查是否提供了选项参数,如果没有,则输出错误信息并终止程序。
检查是否是主节点,如果不是,则输出错误信息并终止程序,因为 -F 选项只能在主节点中指定。
检查是否已经达到外部同步目录的最大数量限制,如果是,则输出错误信息并终止程序。
将外部同步目录添加到 foreign_syncs 数组中,并去除路径末尾的斜杠。*/
if (!optarg) { FATAL("Missing path for -F"); }
if (!afl->is_main_node) {
FATAL(
"Option -F can only be specified after the -M option for the "
"main fuzzer of a fuzzing campaign");
}
if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) {
FATAL("Maximum %u entried of -F option can be specified",
FOREIGN_SYNCS_MAX);
}
afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg;
while (afl->foreign_syncs[afl->foreign_sync_cnt]
.dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) -
1] == '/') {
afl->foreign_syncs[afl->foreign_sync_cnt]
.dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - 1] =
0;
}
afl->foreign_sync_cnt++;
break;
case 'f': /* target file */
/*-f 选项
功能:用于指定目标程序读取的文件路径。
逻辑:
检查是否已经设置了输出文件,如果是,则输出错误信息并终止程序,表示不支持多个 -f 选项。
将输出文件路径设置为选项参数指定的值。
设置 use_stdin 标志为 0表示不使用标准输入。
设置 default_output 标志为 0表示不使用默认输出。*/
if (afl->fsrv.out_file) { FATAL("Multiple -f options not supported"); }
afl->fsrv.out_file = ck_strdup(optarg);
afl->fsrv.use_stdin = 0;
default_output = 0;
break;
case 'x': /* dictionary */
/*-x 选项
功能:用于指定字典文件路径,用于辅助模糊测试。
逻辑:
检查是否已经设置了超过四个字典文件,如果是,则输出错误信息并终止程序,表示不支持超过四个 -x 选项。
将字典文件路径添加到 extras_dir 数组中,并增加 extras_dir_cnt 计数。*/
if (extras_dir_cnt >= 4) {
FATAL("More than four -x options are not supported");
}
extras_dir[extras_dir_cnt++] = optarg;
break;
case 't': { /* timeout */
/*-t 选项
功能:用于设置每次运行的超时时间。
逻辑:
检查是否已经设置了超时时间,如果是,则输出错误信息并终止程序,表示不支持多个 -t 选项。
解析选项参数,提取超时时间值和可能的后缀(如 +)。
如果超时时间值小于 5 毫秒,则输出错误信息并终止程序,表示超时时间过低。
根据后缀设置 timeout_given 标志,+ 表示自动计算超时时间。*/
u8 suffix = 0;
if (afl->timeout_given) { FATAL("Multiple -t options not supported"); }
if (!optarg ||
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 */
/*-m 选项
功能:用于设置子进程的内存限制。
逻辑:
检查是否已经设置了内存限制,如果是,则输出警告信息,表示将覆盖之前的 -m 选项。
解析选项参数,提取内存限制值和可能的单位后缀(如 T、G、k、M
根据单位后缀将内存限制值转换为相应的字节数。
如果内存限制值小于 5 字节,则输出错误信息并终止程序,表示内存限制过低。
在 32 位系统上,如果内存限制值大于 2000 MB则输出错误信息并终止程序表示内存限制超出范围。*/
u8 suffix = 'M';
if (mem_limit_given) {
WARNF("Overriding previous -m option.");
} else {
mem_limit_given = 1;
}
if (!optarg) { FATAL("Wrong usage of -m"); }
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 && 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':
case 'D': /* old deterministic */
/*-d 和 -D 选项
功能:这两个选项已被弃用,用于旧的确定性模糊测试。
逻辑:
输出警告信息,提示用户新的增强确定性模糊测试已默认激活,如果需要禁用,请使用 -z 选项。*/
WARNF(
"Parameters -d and -D are deprecated, a new enhanced deterministic "
"fuzzing is active by default, to disable it use -z");
break;
case 'z': /* no deterministic */
/*-z 选项
功能:用于禁用确定性模糊测试。
逻辑:
设置 skip_deterministic 标志为 1表示跳过确定性模糊测试阶段。*/
afl->skip_deterministic = 1;
break;
case 'B': /* load bitmap */
/*-B 选项
功能:用于加载位图,这是一个未公开的选项。
逻辑:
如果已经设置了 in_bitmap则输出错误信息并终止程序表示不支持多个 -B 选项。
将 in_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;
break;
case 'C': /* crash mode */
/*-C 选项
功能:用于设置崩溃模式。
逻辑:
如果已经设置了 crash_mode则输出错误信息并终止程序表示不支持多个 -C 选项。
将 crash_mode 设置为 FSRV_RUN_CRASH表示启用崩溃模式。*/
if (afl->crash_mode) { FATAL("Multiple -C options not supported"); }
afl->crash_mode = FSRV_RUN_CRASH;
break;
case 'n': /* dumb mode */
/*-n 选项
功能:用于设置非插桩模式。
逻辑:
检查是否是主节点或次级节点,如果是,则输出错误信息并终止程序,因为非插桩模式不支持与 -M 或 -S 一起使用。
检查是否已经设置了 non_instrumented_mode如果是则输出错误信息并终止程序表示不支持多个 -n 选项。
根据环境变量 AFL_DUMB_FORKSRV 的设置,将 non_instrumented_mode 设置为 1 或 2表示启用非插桩模式。*/
if (afl->is_main_node || afl->is_secondary_node) {
FATAL("Non instrumented mode is not supported with -M / -S");
}
if (afl->non_instrumented_mode) {
FATAL("Multiple -n options not supported");
}
if (afl->afl_env.afl_dumb_forksrv) {
afl->non_instrumented_mode = 2;
} else {
afl->non_instrumented_mode = 1;
}
break;
case 'T': /* banner */
/*-T 选项
功能:用于设置横幅。
逻辑:
如果已经设置了 use_banner则输出错误信息并终止程序表示不支持多个 -T 选项。
将 use_banner 设置为选项参数指定的横幅文本。*/
if (afl->use_banner) { FATAL("Multiple -T options not supported"); }
afl->use_banner = optarg;
break;
#ifdef __linux__
case 'X': /* NYX mode */
/*-X 和 -Y 选项(仅限 Linux
功能:用于设置 Nyx 模式。
逻辑:
-X 选项:设置 Nyx 独立模式。
检查是否已经设置了 nyx_mode如果是则输出错误信息并终止程序表示不支持多个 -X 选项。
设置 nyx_parent、nyx_standalone 和 nyx_mode 标志,表示启用 Nyx 独立模式。
-Y 选项:设置 Nyx 分布式模式。
检查是否已经设置了 nyx_mode如果是则输出错误信息并终止程序表示不支持多个 -Y 选项。
设置 nyx_mode 标志,表示启用 Nyx 分布式模式*/
if (afl->fsrv.nyx_mode) { FATAL("Multiple -X options not supported"); }
afl->fsrv.nyx_parent = true;
afl->fsrv.nyx_standalone = true;
afl->fsrv.nyx_mode = 1;
afl->fsrv.nyx_id = 0;
break;
case 'Y': /* NYX distributed mode */
if (afl->fsrv.nyx_mode) { FATAL("Multiple -Y options not supported"); }
afl->fsrv.nyx_mode = 1;
break;
#else
case 'X':
case 'Y':
FATAL("Nyx mode is only availabe on linux...");
break;
#endif
case 'A': /* CoreSight mode */
/*-A 选项(仅限 ARM64 和 Linux
功能:用于设置 ARM CoreSight 模式。
逻辑:
检查是否是主节点或次级节点,如果是,则输出错误信息并终止程序,因为 ARM CoreSight 模式不支持与 -M 或 -S 一起使用。
检查是否已经设置了 cs_mode如果是则输出错误信息并终止程序表示不支持多个 -A 选项。
设置 cs_mode 标志,表示启用 ARM CoreSight 模式。*/
#if !defined(__aarch64__) || !defined(__linux__)
FATAL("-A option is not supported on this platform");
#endif
if (afl->is_main_node || afl->is_secondary_node) {
FATAL("ARM CoreSight mode is not supported with -M / -S");
}
if (afl->fsrv.cs_mode) { FATAL("Multiple -A options not supported"); }
afl->fsrv.cs_mode = 1;
break;
case 'O': /* FRIDA mode */
/*-O 选项
功能:用于设置 FRIDA 模式。
逻辑:
检查是否已经设置了 frida_mode如果是则输出错误信息并终止程序表示不支持多个 -O 选项。
设置 frida_mode 标志,表示启用 FRIDA 模式。
检查环境变量 AFL_USE_FASAN如果设置则设置 frida_asan 标志为 1表示使用 FRIDA 地址制裁模式。*/
if (afl->fsrv.frida_mode) {
FATAL("Multiple -O options not supported");
}
afl->fsrv.frida_mode = 1;
if (get_afl_env("AFL_USE_FASAN")) { afl->fsrv.frida_asan = 1; }
break;
case 'Q': /* QEMU mode */
/*-Q 选项
功能:用于启用 QEMU 模式。
逻辑:
检查是否已经设置了 qemu_mode如果是则输出错误信息并终止程序表示不支持多个 -Q 选项。
设置 qemu_mode 标志为 1表示启用 QEMU 模式。
如果没有指定内存限制,则将 mem_limit 设置为 MEM_LIMIT_QEMU即 QEMU 模式的默认内存限制。*/
if (afl->fsrv.qemu_mode) { FATAL("Multiple -Q options not supported"); }
afl->fsrv.qemu_mode = 1;
if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; }
break;
case 'N': /* Unicorn mode */
/*-N 选项
功能:用于设置不删除模糊测试输入文件的标志。
逻辑:
检查是否已经设置了 no_unlink如果是则输出错误信息并终止程序表示不支持多个 -N 选项。
设置 no_unlink 标志为 true表示在模糊测试过程中不删除输入文件。*/
if (afl->no_unlink) { FATAL("Multiple -N options not supported"); }
afl->fsrv.no_unlink = (afl->no_unlink = true);
break;
case 'U': /* Unicorn mode */
/*-U 选项
功能:用于启用 Unicorn 模式。
逻辑:
检查是否已经设置了 unicorn_mode如果是则输出错误信息并终止程序表示不支持多个 -U 选项。
设置 unicorn_mode 标志为 1表示启用 Unicorn 模式。
如果没有指定内存限制,则将 mem_limit 设置为 MEM_LIMIT_UNICORN即 Unicorn 模式的默认内存限制。
*/
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 */
/*-W 选项
功能:用于启用 Wine+QEMU 模式。
逻辑:
检查是否已经设置了 use_wine如果是则输出错误信息并终止程序表示不支持多个 -W 选项。
设置 qemu_mode 标志为 1表示启用 QEMU 模式。
设置 use_wine 标志为 1表示启用 Wine 模式。
如果没有指定内存限制,则将 mem_limit 设置为 0即不限制内存。*/
if (afl->use_wine) { FATAL("Multiple -W options not supported"); }
afl->fsrv.qemu_mode = 1;
afl->use_wine = 1;
if (!mem_limit_given) { afl->fsrv.mem_limit = 0; }
break;
case 'V': {
/*-V 选项
功能:用于设置 AFL++ 运行的最大时间。
逻辑:
设置 most_time_key 标志为 1表示启用了运行时间限制。
解析选项参数,提取最大运行时间值。
如果解析失败或参数格式不正确,则输出错误信息并终止程序。*/
afl->most_time_key = 1;
if (!optarg || sscanf(optarg, "%llu", &afl->most_time) < 1 ||
optarg[0] == '-') {
FATAL("Bad syntax used for -V");
}
} break;
case 'E': {
/*-E 选项
功能:用于设置 AFL++ 运行的最大执行次数。
逻辑:
设置 most_execs_key 标志为 1表示启用了执行次数限制。
解析选项参数,提取最大执行次数值。
如果解析失败或参数格式不正确,则输出错误信息并终止程序。*/
afl->most_execs_key = 1;
if (!optarg || sscanf(optarg, "%llu", &afl->most_execs) < 1 ||
optarg[0] == '-') {
FATAL("Bad syntax used for -E");
}
} break;
case 'l': {
/*-l 选项
功能:用于配置 CMPLog 的行为。
逻辑:
检查是否提供了选项参数,如果没有,则输出错误信息并终止程序。
遍历选项参数中的每个字符,根据字符设置相应的 CMPLog 配置:
'0'、'1':设置 CMPLog 级别为 1。
'2':设置 CMPLog 级别为 2。
'3':设置 CMPLog 级别为 3并禁用修剪功能。
'a'、'A':启用算术求解。
's'、'S':启用缩放求解。
't'、'T':启用变换求解。
'x'、'X':启用极端变换求解。
'r'、'R':启用随机颜色化。
如果 CMPLog 级别达到最大值 CMPLOG_LVL_MAX则将 cmplog_max_filesize 设置为 MAX_FILE即最大文件大小限制。*/
if (!optarg) { FATAL("missing parameter for 'l'"); }
char *c = optarg;
while (*c) {
switch (*c) {
case '0':
case '1':
afl->cmplog_lvl = 1;
break;
case '2':
afl->cmplog_lvl = 2;
break;
case '3':
afl->cmplog_lvl = 3;
if (!afl->disable_trim) {
ACTF("Deactivating trimming due CMPLOG level 3");
afl->disable_trim = 1;
}
break;
case 'a':
case 'A':
afl->cmplog_enable_arith = 1;
break;
case 's':
case 'S':
afl->cmplog_enable_scale = 1;
break;
case 't':
case 'T':
afl->cmplog_enable_transform = 1;
break;
case 'x':
case 'X':
afl->cmplog_enable_xtreme_transform = 1;
break;
case 'r':
case 'R':
afl->cmplog_random_colorization = 1;
break;
default:
FATAL("Unknown option value '%c' in -l %s", *c, optarg);
}
++c;
}
if (afl->cmplog_lvl == CMPLOG_LVL_MAX) {
afl->cmplog_max_filesize = MAX_FILE;
}
} break;
case 'L': { /* MOpt mode */
/*-L 选项
功能:用于设置 MOpt 模式的时间限制,以分钟为单位。
逻辑:
检查是否已经设置了 limit_time_sig如果是则输出错误信息并终止程序表示不支持多个 -L 选项。
设置 havoc_max_mult 为 HAVOC_MAX_MULT_MOPT这是 MOpt 模式下的 Havoc 变异次数乘数。
解析选项参数,提取时间限制值。
如果解析失败,则输出错误信息并终止程序。
如果时间限制值为 -1则将 limit_time_sig 设置为 -1表示立即进入 MOpt 模式,并将 limit_time_puppet 设置为 0。
如果时间限制值小于 0 但不等于 -1则输出错误信息并终止程序因为时间限制值必须在 0 到 2000000 之间或为 -1。
否则,将 limit_time_sig 设置为 1表示时间限制有效。
设置 old_seed_selection 为 1表示使用旧的种子选择方式。
将时间限制值转换为毫秒(乘以 60 * 1000并检查是否发生溢出。
初始化 MOpt 模式相关的变量,包括 swarm_now、key_puppet、g_now、w_now 等。
遍历每个“swarm”群体初始化相关的变量如 swarm_fitness、stage_finds_puppet、probability_now、x_now、v_now、L_best、G_best、eff_best 等。
计算每个操作符的概率和速度。
确保概率值在合理范围内。
输出警告信息,提示用户 MOpt 模式不再被维护,且不如正常的 Havoc 模式有效。*/
if (afl->limit_time_sig) { FATAL("Multiple -L options not supported"); }
afl->havoc_max_mult = HAVOC_MAX_MULT_MOPT;
if (sscanf(optarg, "%d", &afl->limit_time_puppet) < 1) {
FATAL("Bad syntax used for -L");
}
if (afl->limit_time_puppet == -1) {
afl->limit_time_sig = -1;
afl->limit_time_puppet = 0;
} else if (afl->limit_time_puppet < 0) {
FATAL("-L value must be between 0 and 2000000 or -1");
} else {
afl->limit_time_sig = 1;
}
afl->old_seed_selection = 1;
u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000;
if ((s32)limit_time_puppet2 < afl->limit_time_puppet) {
FATAL("limit_time overflow");
}
afl->limit_time_puppet = limit_time_puppet2;
afl->swarm_now = 0;
if (afl->limit_time_puppet == 0) { afl->key_puppet = 1; }
int j;
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 (j = 0; j < operator_num; ++j) {
afl->stage_finds_puppet[tmp_swarm][j] = 0;
afl->probability_now[tmp_swarm][j] = 0.0;
afl->x_now[tmp_swarm][j] =
((double)(random() % 7000) * 0.0001 + 0.1);
total_puppet_temp += afl->x_now[tmp_swarm][j];
afl->v_now[tmp_swarm][j] = 0.1;
afl->L_best[tmp_swarm][j] = 0.5;
afl->G_best[j] = 0.5;
afl->eff_best[tmp_swarm][j] = 0.0;
}
for (j = 0; j < operator_num; ++j) {
afl->stage_cycles_puppet_v2[tmp_swarm][j] =
afl->stage_cycles_puppet[tmp_swarm][j];
afl->stage_finds_puppet_v2[tmp_swarm][j] =
afl->stage_finds_puppet[tmp_swarm][j];
afl->x_now[tmp_swarm][j] =
afl->x_now[tmp_swarm][j] / total_puppet_temp;
}
double x_temp = 0.0;
for (j = 0; j < operator_num; ++j) {
afl->probability_now[tmp_swarm][j] = 0.0;
afl->v_now[tmp_swarm][j] =
afl->w_now * afl->v_now[tmp_swarm][j] +
RAND_C *
(afl->L_best[tmp_swarm][j] - afl->x_now[tmp_swarm][j]) +
RAND_C * (afl->G_best[j] - afl->x_now[tmp_swarm][j]);
afl->x_now[tmp_swarm][j] += afl->v_now[tmp_swarm][j];
if (afl->x_now[tmp_swarm][j] > v_max) {
afl->x_now[tmp_swarm][j] = v_max;
} else if (afl->x_now[tmp_swarm][j] < v_min) {
afl->x_now[tmp_swarm][j] = v_min;
}
x_temp += afl->x_now[tmp_swarm][j];
}
for (j = 0; j < operator_num; ++j) {
afl->x_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j] / x_temp;
if (likely(j != 0)) {
afl->probability_now[tmp_swarm][j] =
afl->probability_now[tmp_swarm][j - 1] +
afl->x_now[tmp_swarm][j];
} else {
afl->probability_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j];
}
}
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 (j = 0; j < operator_num; ++j) {
afl->core_operator_finds_puppet[j] = 0;
afl->core_operator_finds_puppet_v2[j] = 0;
afl->core_operator_cycles_puppet[j] = 0;
afl->core_operator_cycles_puppet_v2[j] = 0;
afl->core_operator_cycles_puppet_v3[j] = 0;
}
WARNF(
"Note that the MOpt mode is not maintained and is not as effective "
"as normal havoc mode.");
} break;
case 'h':
show_help++;
break; // not needed
/*-h 选项
功能:用于显示帮助信息。
逻辑:
增加 show_help 计数器的值。
break 语句实际上在这里是多余的,因为 switch 语句的每个 case 都是独立的。
*/
case 'R':
/*-R 选项
功能:用于处理 Radamsa 相关的选项。
逻辑:
输出错误信息并终止程序,提示用户 Radamsa 现在是一个自定义变异器应该使用自定义变异器的方式custom_mutators/radamsa/)来使用。*/
FATAL(
"Radamsa is now a custom mutator, please use that "
"(custom_mutators/radamsa/).");
break;
/*默认情况
功能:处理未匹配的选项。
逻辑:
如果 show_help 未被设置,则将其设置为 1表示需要显示帮助信息。*/
default:
if (!show_help) { show_help = 1; }
}
}
/*同步 ID 检查
功能:检查同步 ID 是否为保留名称。
逻辑:
如果 sync_id 被设置为 "addseeds",则输出错误信息并终止程序,因为 "addseeds" 是一个保留名称,不能用作同步 ID*/
if (afl->sync_id && strcmp(afl->sync_id, "addseeds") == 0) {
FATAL("-M/-S name 'addseeds' is a reserved name, choose something else");
}
/*主节点和功率调度检查
功能:检查主节点的功率调度设置。
逻辑:
如果当前是主节点is_main_node 为 1并且功率调度不是 FAST 或 EXPLORE则输出错误信息并终止程序因为 -M 选项仅与 FAST 和 EXPLORE 功率调度兼容*/
if (afl->is_main_node == 1 && afl->schedule != FAST &&
afl->schedule != EXPLORE) {
FATAL("-M is compatible only with fast and explore -p power schedules");
}
/*参数检查和帮助信息显示
功能:检查命令行参数的完整性,并在需要时显示帮助信息。
逻辑:
如果 optind 等于 argc表示没有剩余的命令行参数、输入目录未设置、输出目录未设置或 show_help 被设置,则调用 usage 函数显示帮助信息并退出程序。*/
if (optind == argc || !afl->in_dir || !afl->out_dir || show_help) {
usage(argv[0], show_help);
}
if (unlikely(afl->afl_env.afl_persistent_record)) {
#ifdef AFL_PERSISTENT_RECORD
/*持久记录配置检查
功能:检查 AFL_PERSISTENT_RECORD 环境变量的设置。
逻辑:
如果 afl_env.afl_persistent_record 被设置:
如果 AFL++ 是使用 AFL_PERSISTENT_RECORD 编译的:
将 fsrv.persistent_record 设置为环境变量的值。
如果设置的值小于 2则输出错误信息并终止程序因为 AFL_PERSISTENT_RECORD 的值必须至少为 2推荐值为 100 或 1000。
如果 AFL++ 未使用 AFL_PERSISTENT_RECORD 编译,则输出错误信息并终止程序,提示 AFL++ 未启用 AFL_PERSISTENT_RECORD 支持。*/
afl->fsrv.persistent_record = atoi(afl->afl_env.afl_persistent_record);
if (afl->fsrv.persistent_record < 2) {
FATAL(
"AFL_PERSISTENT_RECORD value must be be at least 2, recommended is "
"100 or 1000.");
}
#else
FATAL(
"afl-fuzz was not compiled with AFL_PERSISTENT_RECORD enabled in "
"config.h!");
#endif
}
if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260;
/*内存限制调整
逻辑:
如果设置了内存限制并且启用了 CMPLog 模式,则将内存限制增加 260 字节。这可能是为了确保 CMPLog 模式有足够的内存来存储额外的信息。*/
OKF("AFL++ is maintained by Marc \"van Hauser\" Heuse, Dominik Maier, Andrea "
"Fioraldi and Heiko \"hexcoder\" Eißfeldt");
OKF("AFL++ is open source, get it at "
"https://github.com/AFLplusplus/AFLplusplus");
OKF("NOTE: AFL++ >= v3 has changed defaults and behaviours - see README.md");
/*版本信息显示
逻辑:
输出 AFL++ 的维护者信息,包括 Marc "van Hauser" Heuse、Dominik Maier、Andrea Fioraldi 和 Heiko "hexcoder" Eißfeldt。
提示 AFL++ 是开源软件,并提供 GitHub 仓库链接。
提醒用户 AFL++ 从版本 3 开始改变了默认设置和行为,建议查看 README.md 文件了解详细信息。*/
#ifdef __linux__
if (afl->fsrv.nyx_mode) {
OKF("AFL++ Nyx mode is enabled (developed and maintained by Sergej "
"Schumilo)");
OKF("Nyx is open source, get it at https://github.com/Nyx-Fuzz");
}
/*Nyx 模式信息显示(仅限 Linux
逻辑:
如果启用了 Nyx 模式,输出 Nyx 模式的信息,包括开发者和维护者 Sergej Schumilo以及 Nyx 的 GitHub 仓库链接。*/
#endif
// silently disable deterministic mutation if custom mutators are used
if (!afl->skip_deterministic && afl->afl_env.afl_custom_mutator_only) {
/*确定性变异禁用
逻辑:
如果启用了自定义变异器且未禁用确定性变异,则自动禁用确定性变异。这是因为自定义变异器可能已经包含了确定性变异的功能。*/
afl->skip_deterministic = 1;
}
if (afl->fixed_seed) {
/*固定种子信息显示
逻辑:
如果设置了固定种子,则输出正在使用的固定种子值。*/
OKF("Running with fixed seed: %u", (u32)afl->init_seed);
}
#if defined(__SANITIZE_ADDRESS__)
if (afl->fsrv.mem_limit) {
WARNF("in the ASAN build we disable all memory limits");
afl->fsrv.mem_limit = 0;
}
#endif
/*信号配置
逻辑:
调用 configure_afl_kill_signals 函数配置 AFL++ 的信号处理,设置子进程和 fork 服务器的终止信号。如果启用了 QEMU、Unicorn、fauxsrv 或 Nyx 模式,则使用 SIGKILL 信号,否则使用 SIGTERM 信号。*/
configure_afl_kill_signals(
&afl->fsrv, afl->afl_env.afl_child_kill_signal,
afl->afl_env.afl_fsrv_kill_signal,
(afl->fsrv.qemu_mode || afl->unicorn_mode || afl->fsrv.use_fauxsrv
#ifdef __linux__
|| afl->fsrv.nyx_mode
#endif
)
? SIGKILL
: SIGTERM);
/*信号处理设置
逻辑:
调用 setup_signal_handlers 函数设置 AFL++ 的信号处理程序,以便在接收到特定信号时执行相应的操作。*/
setup_signal_handlers();
check_asan_opts(afl);
/*ASAN 构建内存限制禁用(仅限 ASAN 构建)
逻辑:
如果 AFL++ 是使用 ASAN 构建的,并且设置了内存限制,则输出警告信息并禁用内存限制。这是因为 ASAN 可能需要更多的内存来检测内存问题。*/
afl->power_name = power_names[afl->schedule];
/*ASAN 选项检查
逻辑:
调用 check_asan_opts 函数检查 ASAN 相关的选项设置,确保 AFL++ 能够与 ASAN 正常配合使用。*/
/*功率调度名称设置
逻辑:
根据当前的功率调度策略,从 power_names 数组中获取对应的名称,并将其存储在 afl->power_name 中。*/
if (!afl->non_instrumented_mode && !afl->sync_id) {
auto_sync = 1;
afl->sync_id = ck_strdup("default");
afl->is_secondary_node = 1;
OKF("No -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id);
}
/*Nyx 模式同步配置(仅限 Linux
逻辑:
如果启用了 Nyx 模式:
如果是 Nyx 独立模式且同步 ID 不是 "default",则输出错误信息并终止程序,因为独立模式不支持分布式模糊测试。
如果是 Nyx 分布式模式:
如果是主节点且同步 ID 不是 "0",则输出错误信息并终止程序,因为主节点的 ID 必须是 "0"。
如果是次级节点,则解析同步 ID 并进行有效性检查。如果 ID 不是有效的数字或小于 1则输出错误信息并终止程序。*/
#ifdef __linux__
if (afl->fsrv.nyx_mode) {
if (afl->fsrv.nyx_standalone && strcmp(afl->sync_id, "default") != 0) {
FATAL(
"distributed fuzzing is not supported in this Nyx mode (use -Y "
"instead)");
}
if (!afl->fsrv.nyx_standalone) {
if (afl->is_main_node) {
if (strcmp("0", afl->sync_id) != 0) {
FATAL(
"for Nyx -Y mode, the Main (-M) parameter has to be set to 0 (-M "
"0)");
}
afl->fsrv.nyx_parent = true;
afl->fsrv.nyx_id = 0;
}
if (afl->is_secondary_node) {
long nyx_id = strtol(afl->sync_id, NULL, 10);
if (nyx_id == 0 || nyx_id == LONG_MAX) {
FATAL(
"for Nyx -Y mode, the Secondary (-S) parameter has to be a "
"numeric value and >= 1 (e.g. -S 1)");
}
afl->fsrv.nyx_id = nyx_id;
}
}
}
#endif
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->non_instrumented_mode) {
if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); }
if (afl->fsrv.frida_mode) { FATAL("-O and -n are mutually exclusive"); }
if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); }
if (afl->fsrv.cs_mode) { FATAL("-A and -n are mutually exclusive"); }
if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); }
}
setenv("__AFL_OUT_DIR", afl->out_dir, 1);
if (get_afl_env("AFL_DISABLE_TRIM") || get_afl_env("AFL_NO_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 (unlikely(afl->afl_env.afl_statsd)) { statsd_setup_format(afl); }
if (!afl->use_banner) { afl->use_banner = argv[optind]; }
if (afl->shm.cmplog_mode && strcmp("0", afl->cmplog_binary) == 0) {
afl->cmplog_binary = strdup(argv[optind]);
}
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 SEEK:
OKF("Using seek power schedule (SEEK)");
break;
case EXPLORE:
OKF("Using exploration-based constant power schedule (EXPLORE)");
break;
default:
FATAL("Unknown power schedule");
break;
}
if (afl->shm.cmplog_mode) { OKF("CmpLog level: %u", afl->cmplog_lvl); }
/* Dynamically allocate memory for AFLFast schedules */
if (afl->schedule >= FAST && afl->schedule <= RARE) {
afl->n_fuzz = ck_alloc(N_FUZZ_SIZE * sizeof(u32));
}
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_EXPAND_HAVOC_NOW")) { afl->expand_havoc = 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) {
s32 hang_tmout = atoi(afl->afl_env.afl_hang_tmout);
if (hang_tmout < 1) { FATAL("Invalid value for AFL_HANG_TMOUT"); }
afl->hang_tmout = (u32)hang_tmout;
}
if (afl->afl_env.afl_exit_on_time) {
u64 exit_on_time = atoi(afl->afl_env.afl_exit_on_time);
afl->exit_on_time = (u64)exit_on_time * 1000;
}
if (afl->afl_env.afl_max_det_extras) {
s32 max_det_extras = atoi(afl->afl_env.afl_max_det_extras);
if (max_det_extras < 1) { FATAL("Invalid value for AFL_MAX_DET_EXTRAS"); }
afl->max_det_extras = (u32)max_det_extras;
} else {
afl->max_det_extras = MAX_DET_EXTRAS;
}
if (afl->afl_env.afl_testcache_size) {
afl->q_testcase_max_cache_size =
(u64)atoi(afl->afl_env.afl_testcache_size) * 1048576;
}
if (afl->afl_env.afl_testcache_entries) {
afl->q_testcase_max_cache_entries =
(u32)atoi(afl->afl_env.afl_testcache_entries);
// user_set_cache = 1;
}
if (!afl->afl_env.afl_testcache_size || !afl->afl_env.afl_testcache_entries) {
afl->afl_env.afl_testcache_entries = 0;
afl->afl_env.afl_testcache_size = 0;
}
if (!afl->q_testcase_max_cache_size) {
ACTF(
"No testcache was configured. it is recommended to use a testcache, it "
"improves performance: set AFL_TESTCACHE_SIZE=(value in MB)");
} else if (afl->q_testcase_max_cache_size < 2 * MAX_FILE) {
FATAL("AFL_TESTCACHE_SIZE must be set to %ld or more, or 0 to disable",
(2 * MAX_FILE) % 1048576 == 0 ? (2 * MAX_FILE) / 1048576
: 1 + ((2 * MAX_FILE) / 1048576));
} else {
OKF("Enabled testcache with %llu MB",
afl->q_testcase_max_cache_size / 1048576);
}
if (afl->afl_env.afl_forksrv_init_tmout) {
afl->fsrv.init_tmout = atoi(afl->afl_env.afl_forksrv_init_tmout);
if (!afl->fsrv.init_tmout) {
FATAL("Invalid value of AFL_FORKSRV_INIT_TMOUT");
}
} else {
afl->fsrv.init_tmout = afl->fsrv.exec_tmout * FORK_WAIT_MULT;
}
if (afl->afl_env.afl_crash_exitcode) {
long exitcode = strtol(afl->afl_env.afl_crash_exitcode, NULL, 10);
if ((!exitcode && (errno == EINVAL || errno == ERANGE)) ||
exitcode < -127 || exitcode > 128) {
FATAL("Invalid crash exitcode, expected -127 to 128, but got %s",
afl->afl_env.afl_crash_exitcode);
}
afl->fsrv.uses_crash_exitcode = true;
// WEXITSTATUS is 8 bit unsigned
afl->fsrv.crash_exitcode = (u8)exitcode;
}
if (afl->non_instrumented_mode == 2 && afl->no_forkserver) {
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
}
// Marker: ADD_TO_INJECTIONS
if (getenv("AFL_LLVM_INJECTIONS_ALL") || getenv("AFL_LLVM_INJECTIONS_SQL") ||
getenv("AFL_LLVM_INJECTIONS_LDAP") || getenv("AFL_LLVM_INJECTIONS_XSS")) {
OKF("Adding injection tokens to dictionary.");
if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
getenv("AFL_LLVM_INJECTIONS_SQL")) {
add_extra(afl, "'\"\"'", 4);
}
if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
getenv("AFL_LLVM_INJECTIONS_LDAP")) {
add_extra(afl, "*)(1=*))(|", 10);
}
if (getenv("AFL_LLVM_INJECTIONS_ALL") ||
getenv("AFL_LLVM_INJECTIONS_XSS")) {
add_extra(afl, "1\"><\"", 5);
}
}
OKF("Generating fuzz data with a length of min=%u max=%u", afl->min_length,
afl->max_length);
u32 min_alloc = MAX(64U, afl->min_length);
afl_realloc(AFL_BUF_PARAM(in_scratch), min_alloc);
afl_realloc(AFL_BUF_PARAM(in), min_alloc);
afl_realloc(AFL_BUF_PARAM(out_scratch), min_alloc);
afl_realloc(AFL_BUF_PARAM(out), min_alloc);
afl_realloc(AFL_BUF_PARAM(eff), min_alloc);
afl_realloc(AFL_BUF_PARAM(ex), min_alloc);
afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver;
afl->fsrv.max_length = afl->max_length;
#ifdef __linux__
if (!afl->fsrv.nyx_mode) {
check_crash_handling();
check_cpu_governor(afl);
} else {
u8 *libnyx_binary = find_afl_binary(argv[0], "libnyx.so");
afl->fsrv.nyx_handlers = afl_load_libnyx_plugin(libnyx_binary);
if (afl->fsrv.nyx_handlers == NULL) {
FATAL("failed to initialize libnyx.so...");
}
}
#else
check_crash_handling();
check_cpu_governor(afl);
#endif
#ifdef __APPLE__
setenv("DYLD_NO_PIE", "1", 0);
#endif
if (getenv("LD_PRELOAD")) {
WARNF(
"LD_PRELOAD is set, are you sure that is what you want to do "
"instead of using AFL_PRELOAD?");
}
if (afl->afl_env.afl_preload) {
if (afl->fsrv.qemu_mode) {
/* afl-qemu-trace takes care of converting AFL_PRELOAD. */
} else if (afl->fsrv.frida_mode) {
afl_preload = getenv("AFL_PRELOAD");
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
OKF("Injecting %s ...", frida_binary);
if (afl_preload) {
if (afl->fsrv.frida_asan) {
OKF("Using Frida Address Sanitizer Mode");
if (afl->fsrv.mem_limit) {
WARNF(
"in the Frida Address Sanitizer Mode we disable all memory "
"limits");
afl->fsrv.mem_limit = 0;
}
fasan_check_afl_preload(afl_preload);
setenv("ASAN_OPTIONS", "detect_leaks=false", 1);
}
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
OKF("Injecting %s ...", frida_binary);
frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
ck_free(frida_binary);
setenv("LD_PRELOAD", frida_afl_preload, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
}
} else {
/* CoreSight mode uses the default behavior. */
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
}
} else if (afl->fsrv.frida_mode) {
if (afl->fsrv.frida_asan) {
OKF("Using Frida Address Sanitizer Mode");
FATAL(
"Address Sanitizer DSO must be loaded using AFL_PRELOAD in Frida "
"Address Sanitizer Mode");
} else {
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
OKF("Injecting %s ...", frida_binary);
setenv("LD_PRELOAD", frida_binary, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
ck_free(frida_binary);
}
}
if (getenv("AFL_LD_PRELOAD")) {
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
}
if (afl->afl_env.afl_target_env &&
!extract_and_set_env(afl->afl_env.afl_target_env)) {
FATAL("Bad value of AFL_TARGET_ENV");
}
save_cmdline(afl, argc, argv);
check_if_tty(afl);
if (afl->afl_env.afl_force_ui) { afl->not_on_tty = 0; }
get_core_count(afl);
atexit(at_exit);
setup_dirs_fds(afl);
#ifdef HAVE_AFFINITY
bind_to_free_cpu(afl);
#endif /* HAVE_AFFINITY */
#ifdef __linux__
if (afl->fsrv.nyx_mode && afl->fsrv.nyx_bind_cpu_id == 0xFFFFFFFF) {
afl->fsrv.nyx_bind_cpu_id = 0;
}
#endif
#ifdef __HAIKU__
/* Prioritizes performance over power saving */
set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
#endif
#ifdef __APPLE__
if (pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) != 0) {
WARNF("general thread priority settings failed");
}
#endif
init_count_class16();
if (afl->is_main_node && check_main_node_exists(afl) == 1) {
WARNF("it is wasteful to run more than one main node!");
sleep(1);
} else if (!auto_sync && afl->is_secondary_node &&
check_main_node_exists(afl) == 0) {
WARNF(
"no -M main node found. It is recommended to run exactly one main "
"instance.");
sleep(1);
}
#ifdef RAND_TEST_VALUES
u32 counter;
for (counter = 0; counter < 100000; counter++)
printf("DEBUG: rand %06d is %u\n", counter, rand_below(afl, 65536));
#endif
if (!getenv("AFL_CUSTOM_INFO_PROGRAM")) {
setenv("AFL_CUSTOM_INFO_PROGRAM", argv[optind], 1);
}
if (!getenv("AFL_CUSTOM_INFO_PROGRAM_INPUT") && afl->fsrv.out_file) {
setenv("AFL_CUSTOM_INFO_PROGRAM_INPUT", afl->fsrv.out_file, 1);
}
if (!getenv("AFL_CUSTOM_INFO_PROGRAM_ARGV")) {
u8 envbuf[8096] = "", tmpbuf[8096] = "";
for (s32 i = optind + 1; i < argc; ++i) {
strcpy(tmpbuf, envbuf);
if (strchr(argv[i], ' ') && !strchr(argv[i], '"') &&
!strchr(argv[i], '\'')) {
if (!strchr(argv[i], '\'')) {
snprintf(envbuf, sizeof(tmpbuf), "%s '%s'", tmpbuf, argv[i]);
} else {
snprintf(envbuf, sizeof(tmpbuf), "%s \"%s\"", tmpbuf, argv[i]);
}
} else {
snprintf(envbuf, sizeof(tmpbuf), "%s %s", tmpbuf, argv[i]);
}
}
setenv("AFL_CUSTOM_INFO_PROGRAM_ARGV", envbuf + 1, 1);
}
if (!getenv("AFL_CUSTOM_INFO_OUT")) {
setenv("AFL_CUSTOM_INFO_OUT", afl->out_dir, 1); // same as __AFL_OUT_DIR
}
setup_custom_mutators(afl);
if (afl->afl_env.afl_custom_mutator_only) {
if (!afl->custom_mutators_count) {
if (afl->shm.cmplog_mode) {
WARNF(
"No custom mutator loaded, using AFL_CUSTOM_MUTATOR_ONLY is "
"pointless and only allowed now to allow experiments with CMPLOG.");
} else {
FATAL(
"No custom mutator loaded but AFL_CUSTOM_MUTATOR_ONLY specified.");
}
}
/* This ensures we don't proceed to havoc/splice */
afl->custom_only = 1;
/* Ensure we also skip all deterministic steps */
afl->skip_deterministic = 1;
}
if (afl->custom_mutators_count && afl->afl_env.afl_custom_mutator_late_send) {
u32 count_send = 0;
LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
if (el->afl_custom_fuzz_send) {
if (count_send) {
FATAL(
"You can only have one custom send() function if you are using "
"AFL_CUSTOM_MUTATOR_LATE_SEND!");
}
afl->fsrv.late_send = el->afl_custom_fuzz_send;
afl->fsrv.custom_data_ptr = el->data;
count_send = 1;
}
});
}
if (afl->limit_time_sig > 0 && afl->custom_mutators_count) {
if (afl->custom_only) {
FATAL("Custom mutators are incompatible with MOpt (-L)");
}
u32 custom_fuzz = 0;
LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
if (el->afl_custom_fuzz) { custom_fuzz = 1; }
});
if (custom_fuzz) {
WARNF("afl_custom_fuzz is incompatible with MOpt (-L)");
}
}
/* Simply code if AFL_TMPDIR is used or not */
if (!afl->afl_env.afl_tmpdir) {
afl->tmp_dir = afl->out_dir;
} else {
afl->tmp_dir = afl->afl_env.afl_tmpdir;
}
setup_cmdline_file(afl, argv + optind);
check_binary(afl, argv[optind]);
u64 prev_target_hash = 0;
s32 fast_resume = 0;
#ifdef HAVE_ZLIB
gzFile fr_fd = NULL;
#else
s32 fr_fd = -1;
#endif
if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
u8 fn[PATH_MAX], buf[32];
snprintf(fn, PATH_MAX, "%s/target_hash", afl->out_dir);
s32 fd = open(fn, O_RDONLY);
if (fd >= 0) {
if (read(fd, buf, 32) >= 16) {
sscanf(buf, "%p", (void **)&prev_target_hash);
}
close(fd);
}
}
write_setup_file(afl, argc, argv);
if (afl->in_place_resume && !afl->afl_env.afl_no_fastresume) {
#ifdef __linux__
u64 target_hash = 0;
if (afl->fsrv.nyx_mode) {
nyx_load_target_hash(&afl->fsrv);
target_hash = afl->fsrv.nyx_target_hash64;
} else {
target_hash = get_binary_hash(afl->fsrv.target_path);
}
#else
u64 target_hash = get_binary_hash(afl->fsrv.target_path);
#endif
if ((!target_hash || prev_target_hash != target_hash)
#ifdef __linux__
|| (afl->fsrv.nyx_mode && target_hash == 0)
#endif
) {
ACTF("Target binary is different, cannot perform FAST RESUME!");
} else {
u8 fn[PATH_MAX];
snprintf(fn, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
#ifdef HAVE_ZLIB
if ((fr_fd = ZLIBOPEN(fn, "rb")) != NULL) {
#else
if ((fr_fd = open(fn, O_RDONLY)) >= 0) {
#endif
u8 ver_string[8];
u64 *ver = (u64 *)ver_string;
u64 expect_ver =
afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
if (NZLIBREAD(fr_fd, ver_string, sizeof(ver_string)) !=
sizeof(ver_string))
WARNF("Emtpy fastresume.bin, ignoring, cannot perform FAST RESUME");
else if (expect_ver != *ver)
WARNF(
"Different AFL++ version or feature usage, cannot perform FAST "
"RESUME");
else {
OKF("Will perform FAST RESUME");
fast_resume = 1;
}
} else {
ACTF("fastresume.bin not found, cannot perform FAST RESUME!");
}
// If the fast resume file is not valid we will be unable to start, so
// we remove the file but keep the file descriptor open.
unlink(fn);
}
}
read_testcases(afl, NULL);
pivot_inputs(afl);
if (!afl->timeout_given) { find_timeout(afl); } // only for resumes!
if (afl->afl_env.afl_tmpdir && !afl->in_place_resume) {
char tmpfile[PATH_MAX];
if (afl->file_extension) {
snprintf(tmpfile, PATH_MAX, "%s/.cur_input.%s", afl->tmp_dir,
afl->file_extension);
} else {
snprintf(tmpfile, PATH_MAX, "%s/.cur_input", afl->tmp_dir);
}
/* there is still a race condition here, but well ... */
if (access(tmpfile, F_OK) != -1) {
FATAL(
"AFL_TMPDIR already has an existing temporary input file: %s - if "
"this is not from another instance, then just remove the file.",
tmpfile);
}
}
// read_foreign_testcases(afl, 1); for the moment dont do this
OKF("Loaded a total of %u seeds.", afl->queued_items);
/* If we don't have a file name chosen yet, use a safe default. */
if (!afl->fsrv.out_file) {
u32 j = optind + 1;
while (argv[j]) {
u8 *aa_loc = strstr(argv[j], "@@");
if (aa_loc && !afl->fsrv.out_file) {
afl->fsrv.use_stdin = 0;
default_output = 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;
}
++j;
}
}
if (!afl->fsrv.out_file) { setup_stdio_file(afl); }
if (afl->cmplog_binary) {
if (afl->unicorn_mode) {
FATAL("CmpLog and Unicorn mode are not compatible at the moment, sorry");
}
if (!afl->fsrv.qemu_mode && !afl->fsrv.frida_mode && !afl->fsrv.cs_mode &&
!afl->non_instrumented_mode) {
check_binary(afl, afl->cmplog_binary);
}
}
#ifdef AFL_PERSISTENT_RECORD
if (unlikely(afl->fsrv.persistent_record)) {
if (!getenv(PERSIST_ENV_VAR) && !getenv("AFL_FRIDA_PERSISTENT_ADDR") &&
!getenv("AFL_QEMU_PERSISTENT_ADDR")) {
FATAL(
"Target binary is not compiled/run in persistent mode, "
"AFL_PERSISTENT_RECORD makes no sense.");
}
afl->fsrv.persistent_record_dir = alloc_printf("%s", afl->out_dir);
}
#endif
if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); }
afl->start_time = get_cur_time();
if (afl->fsrv.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 if (afl->fsrv.cs_mode) {
use_argv = get_cs_argv(argv[0], &afl->fsrv.target_path, argc - optind,
argv + optind);
} else {
use_argv = argv + optind;
}
if (afl->non_instrumented_mode || afl->fsrv.qemu_mode ||
afl->fsrv.frida_mode || afl->fsrv.cs_mode || afl->unicorn_mode) {
u32 old_map_size = map_size;
map_size = afl->fsrv.real_map_size = afl->fsrv.map_size = MAP_SIZE;
afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size);
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size);
afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size);
afl->var_bytes = ck_realloc(afl->var_bytes, map_size);
afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *));
afl->clean_trace = ck_realloc(afl->clean_trace, map_size);
afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size);
afl->first_trace = ck_realloc(afl->first_trace, map_size);
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size);
if (old_map_size < map_size) {
memset(afl->var_bytes + old_map_size, 0, map_size - old_map_size);
memset(afl->top_rated + old_map_size, 0, map_size - old_map_size);
memset(afl->clean_trace + old_map_size, 0, map_size - old_map_size);
memset(afl->clean_trace_custom + old_map_size, 0,
map_size - old_map_size);
memset(afl->first_trace + old_map_size, 0, map_size - old_map_size);
memset(afl->map_tmp_buf + old_map_size, 0, map_size - old_map_size);
}
}
afl->argv = use_argv;
afl->fsrv.trace_bits =
afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode);
if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode &&
!afl->unicorn_mode && !afl->fsrv.frida_mode && !afl->fsrv.cs_mode &&
!afl->afl_env.afl_skip_bin_check) {
if (map_size <= DEFAULT_SHMEM_SIZE) {
afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value
char vbuf[16];
snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE);
setenv("AFL_MAP_SIZE", vbuf, 1);
}
u32 new_map_size = afl_fsrv_get_mapsize(
&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child);
// only reinitialize if the map needs to be larger than what we have.
if (map_size < new_map_size) {
OKF("Re-initializing maps to %u bytes", new_map_size);
u32 old_map_size = map_size;
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
afl->top_rated =
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
afl->clean_trace_custom =
ck_realloc(afl->clean_trace_custom, new_map_size);
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
if (old_map_size < new_map_size) {
memset(afl->var_bytes + old_map_size, 0, new_map_size - old_map_size);
memset(afl->top_rated + old_map_size, 0, new_map_size - old_map_size);
memset(afl->clean_trace + old_map_size, 0, new_map_size - old_map_size);
memset(afl->clean_trace_custom + old_map_size, 0,
new_map_size - old_map_size);
memset(afl->first_trace + old_map_size, 0, new_map_size - old_map_size);
memset(afl->map_tmp_buf + old_map_size, 0, new_map_size - old_map_size);
}
afl_fsrv_kill(&afl->fsrv);
afl_shm_deinit(&afl->shm);
afl->fsrv.map_size = new_map_size;
afl->fsrv.trace_bits =
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child);
map_size = new_map_size;
}
}
if (afl->cmplog_binary) {
ACTF("Spawning cmplog forkserver");
afl_fsrv_init_dup(&afl->cmplog_fsrv, &afl->fsrv);
// TODO: this is semi-nice
afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits;
afl->cmplog_fsrv.cs_mode = afl->fsrv.cs_mode;
afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode;
afl->cmplog_fsrv.frida_mode = afl->fsrv.frida_mode;
afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary;
afl->cmplog_fsrv.target_path = afl->fsrv.target_path;
afl->cmplog_fsrv.init_child_func = cmplog_exec_child;
if ((map_size <= DEFAULT_SHMEM_SIZE ||
afl->cmplog_fsrv.map_size < map_size) &&
!afl->non_instrumented_mode && !afl->fsrv.qemu_mode &&
!afl->fsrv.frida_mode && !afl->unicorn_mode && !afl->fsrv.cs_mode &&
!afl->afl_env.afl_skip_bin_check) {
afl->cmplog_fsrv.map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE);
char vbuf[16];
snprintf(vbuf, sizeof(vbuf), "%u", afl->cmplog_fsrv.map_size);
setenv("AFL_MAP_SIZE", vbuf, 1);
}
u32 new_map_size =
afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child);
// only reinitialize when it needs to be larger
if (map_size < new_map_size) {
OKF("Re-initializing maps to %u bytes due cmplog", new_map_size);
u32 old_map_size = map_size;
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
afl->top_rated =
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
afl->clean_trace_custom =
ck_realloc(afl->clean_trace_custom, new_map_size);
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
if (old_map_size < new_map_size) {
memset(afl->var_bytes + old_map_size, 0, new_map_size - old_map_size);
memset(afl->top_rated + old_map_size, 0, new_map_size - old_map_size);
memset(afl->clean_trace + old_map_size, 0, new_map_size - old_map_size);
memset(afl->clean_trace_custom + old_map_size, 0,
new_map_size - old_map_size);
memset(afl->first_trace + old_map_size, 0, new_map_size - old_map_size);
memset(afl->map_tmp_buf + old_map_size, 0, new_map_size - old_map_size);
}
afl_fsrv_kill(&afl->fsrv);
afl_fsrv_kill(&afl->cmplog_fsrv);
afl_shm_deinit(&afl->shm);
afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same
map_size = new_map_size;
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
afl->fsrv.trace_bits =
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits;
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child);
afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child);
}
OKF("CMPLOG forkserver successfully started");
}
load_auto(afl);
if (extras_dir_cnt) {
for (u8 i = 0; i < extras_dir_cnt; i++) {
load_extras(afl, extras_dir[i]);
}
}
if (afl->fsrv.out_file && afl->fsrv.use_shmem_fuzz) {
unlink(afl->fsrv.out_file);
afl->fsrv.out_file = NULL;
afl->fsrv.use_stdin = 0;
close(afl->fsrv.out_fd);
afl->fsrv.out_fd = -1;
if (!afl->unicorn_mode && !afl->fsrv.use_stdin && !default_output) {
WARNF(
"You specified -f or @@ on the command line but the target harness "
"specified fuzz cases via shmem, switching to shmem!");
}
}
deunicode_extras(afl);
dedup_extras(afl);
if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); }
if (unlikely(fast_resume)) {
u64 resume_start = get_cur_time_us();
// if we get here then we should abort on errors
ZLIBREAD(fr_fd, afl->virgin_bits, afl->fsrv.map_size, "virgin_bits");
ZLIBREAD(fr_fd, afl->virgin_tmout, afl->fsrv.map_size, "virgin_tmout");
ZLIBREAD(fr_fd, afl->virgin_crash, afl->fsrv.map_size, "virgin_crash");
ZLIBREAD(fr_fd, afl->var_bytes, afl->fsrv.map_size, "var_bytes");
u8 res[1] = {0};
u8 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
u8 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
u32 r = 8 + afl->fsrv.map_size * 4;
u32 q_len = o_end - o_start;
u32 m_len = (afl->fsrv.map_size >> 3);
struct queue_entry *q;
for (u32 i = 0; i < afl->queued_items; i++) {
q = afl->queue_buf[i];
ZLIBREAD(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
ZLIBREAD(fr_fd, res, 1, "check map");
if (res[0]) {
q->trace_mini = ck_alloc(m_len);
ZLIBREAD(fr_fd, q->trace_mini, m_len, "trace_mini");
r += q_len + m_len + 1;
} else {
r += q_len + 1;
}
afl->total_bitmap_size += q->bitmap_size;
++afl->total_bitmap_entries;
update_bitmap_score(afl, q);
if (q->was_fuzzed) { --afl->pending_not_fuzzed; }
if (q->disabled) {
if (!q->was_fuzzed) { --afl->pending_not_fuzzed; }
--afl->active_items;
}
if (q->var_behavior) { ++afl->queued_variable; }
if (q->favored) {
++afl->queued_favored;
if (!q->was_fuzzed) { ++afl->pending_favored; }
}
}
u8 buf[4];
if (NZLIBREAD(fr_fd, buf, 3) > 0) {
FATAL("invalid trailing data in fastresume.bin");
}
OKF("Successfully loaded fastresume.bin (%u bytes)!", r);
ZLIBCLOSE(fr_fd);
afl->reinit_table = 1;
update_calibration_time(afl, &resume_start);
} else {
// after we have the correct bitmap size we can read the bitmap -B option
// and set the virgin maps
if (afl->in_bitmap) {
read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size);
} else {
memset(afl->virgin_bits, 255, map_size);
}
memset(afl->virgin_tmout, 255, map_size);
memset(afl->virgin_crash, 255, map_size);
if (likely(!afl->afl_env.afl_no_startup_calibration)) {
perform_dry_run(afl);
} else {
ACTF("Skipping initial seed calibration due option override!");
usleep(1000);
}
}
if (afl->q_testcase_max_cache_entries) {
afl->q_testcase_cache =
ck_alloc(afl->q_testcase_max_cache_entries * sizeof(size_t));
if (!afl->q_testcase_cache) { PFATAL("malloc failed for cache entries"); }
}
cull_queue(afl);
// ensure we have at least one seed that is not disabled.
u32 entry, valid_seeds = 0;
for (entry = 0; entry < afl->queued_items; ++entry)
if (!afl->queue_buf[entry]->disabled) { ++valid_seeds; }
if (!afl->pending_not_fuzzed || !valid_seeds) {
FATAL("We need at least one valid input seed that does not crash!");
}
if (afl->timeout_given == 2) { // -t ...+ option
if (valid_seeds == 1) {
WARNF(
"Only one valid seed is present, auto-calculating the timeout is "
"disabled!");
afl->timeout_given = 1;
} else {
u64 max_ms = 0;
for (entry = 0; entry < afl->queued_items; ++entry)
if (!afl->queue_buf[entry]->disabled)
if ((afl->queue_buf[entry]->exec_us / 1000) > max_ms)
max_ms = afl->queue_buf[entry]->exec_us / 1000;
// Add 20% as a safety margin, capped to exec_tmout given in -t option
max_ms *= 1.2;
if (max_ms > afl->fsrv.exec_tmout) max_ms = afl->fsrv.exec_tmout;
// Ensure that there is a sensible timeout even for very fast binaries
if (max_ms < 5) max_ms = 5;
afl->fsrv.exec_tmout = max_ms;
afl->timeout_given = 1;
}
}
show_init_stats(afl);
if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl);
afl->start_time = get_cur_time();
if (afl->in_place_resume || afl->afl_env.afl_autoresume) {
load_stats_file(afl);
}
if (!afl->non_instrumented_mode) { write_stats_file(afl, 0, 0, 0, 0); }
maybe_update_plot_file(afl, 0, 0, 0);
save_auto(afl);
if (afl->stop_soon) { goto stop_fuzzing; }
/* Woop woop woop */
if (!afl->not_on_tty) {
sleep(1);
if (afl->stop_soon) { goto stop_fuzzing; }
}
// (void)nice(-20); // does not improve the speed
#ifdef INTROSPECTION
u32 prev_saved_crashes = 0, prev_saved_tmouts = 0, stat_prev_queued_items = 0;
#endif
u32 prev_queued_items = 0, runs_in_current_cycle = (u32)-1;
u8 skipped_fuzz;
#ifdef INTROSPECTION
char ifn[4096];
snprintf(ifn, sizeof(ifn), "%s/introspection.txt", afl->out_dir);
if ((afl->introspection_file = fopen(ifn, "w")) == NULL) {
PFATAL("could not create '%s'", ifn);
}
setvbuf(afl->introspection_file, NULL, _IONBF, 0);
OKF("Writing mutation introspection to '%s'", ifn);
#endif
// real start time, we reset, so this works correctly with -V
afl->start_time = get_cur_time();
while (likely(!afl->stop_soon)) {
cull_queue(afl);
if (unlikely((!afl->old_seed_selection &&
runs_in_current_cycle > afl->queued_items) ||
(afl->old_seed_selection && !afl->queue_cur))) {
if (unlikely((afl->last_sync_cycle < afl->queue_cycle ||
(!afl->queue_cycle && afl->afl_env.afl_import_first)) &&
afl->sync_id)) {
if (unlikely(!afl->queue_cycle && afl->afl_env.afl_import_first)) {
OKF("Syncing queues from other fuzzer instances first ...");
}
sync_fuzzers(afl);
}
++afl->queue_cycle;
if (afl->afl_env.afl_no_ui) {
ACTF("Entering queue cycle %llu\n", afl->queue_cycle);
}
runs_in_current_cycle = (u32)-1;
afl->cur_skipped_items = 0;
// 1st april fool joke - enable pizza mode
// to not waste time on checking the date we only do this when the
// queue is fully cycled.
time_t cursec = time(NULL);
struct tm *curdate = localtime(&cursec);
if (unlikely(!afl->afl_env.afl_pizza_mode)) {
if (unlikely(curdate->tm_mon == 3 && curdate->tm_mday == 1)) {
afl->pizza_is_served = 1;
} else {
afl->pizza_is_served = 0;
}
}
if (unlikely(afl->old_seed_selection)) {
afl->current_entry = 0;
while (unlikely(afl->current_entry < afl->queued_items &&
afl->queue_buf[afl->current_entry]->disabled)) {
++afl->current_entry;
}
if (afl->current_entry >= afl->queued_items) { afl->current_entry = 0; }
afl->queue_cur = afl->queue_buf[afl->current_entry];
if (unlikely(seek_to)) {
if (unlikely(seek_to >= afl->queued_items)) {
// This should never happen.
FATAL("BUG: seek_to location out of bounds!\n");
}
afl->current_entry = seek_to;
afl->queue_cur = afl->queue_buf[seek_to];
seek_to = 0;
}
}
/* If we had a full queue cycle with no new finds, try
recombination strategies next. */
if (unlikely(afl->queued_items == prev_queued
/* FIXME TODO BUG: && (get_cur_time() - afl->start_time) >=
3600 */
)) {
if (afl->use_splicing) {
++afl->cycles_wo_finds;
if (unlikely(afl->shm.cmplog_mode &&
afl->cmplog_max_filesize < MAX_FILE)) {
afl->cmplog_max_filesize <<= 4;
}
switch (afl->expand_havoc) {
case 0:
// this adds extra splicing mutation options to havoc mode
afl->expand_havoc = 1;
break;
case 1:
// add MOpt mutator
/*
if (afl->limit_time_sig == 0 && !afl->custom_only &&
!afl->python_only) {
afl->limit_time_sig = -1;
afl->limit_time_puppet = 0;
}
*/
afl->expand_havoc = 2;
if (afl->cmplog_lvl && afl->cmplog_lvl < 2) afl->cmplog_lvl = 2;
break;
case 2:
// increase havoc mutations per fuzz attempt
afl->havoc_stack_pow2++;
afl->expand_havoc = 3;
break;
case 3:
// further increase havoc mutations per fuzz attempt
afl->havoc_stack_pow2++;
afl->expand_havoc = 4;
break;
case 4:
afl->expand_havoc = 5;
// if (afl->cmplog_lvl && afl->cmplog_lvl < 3) afl->cmplog_lvl =
// 3;
break;
case 5:
// nothing else currently
break;
}
} else {
#ifndef NO_SPLICING
afl->use_splicing = 1;
#else
afl->use_splicing = 0;
#endif
}
} else {
afl->cycles_wo_finds = 0;
}
#ifdef INTROSPECTION
{
u64 cur_time = get_cur_time();
fprintf(afl->introspection_file,
"CYCLE cycle=%llu cycle_wo_finds=%llu time_wo_finds=%llu "
"expand_havoc=%u queue=%u\n",
afl->queue_cycle, afl->cycles_wo_finds,
afl->longest_find_time > cur_time - afl->last_find_time
? afl->longest_find_time / 1000
: ((afl->start_time == 0 || afl->last_find_time == 0)
? 0
: (cur_time - afl->last_find_time) / 1000),
afl->expand_havoc, afl->queued_items);
}
#endif
if (afl->cycle_schedules) {
/* we cannot mix non-AFLfast schedules with others */
switch (afl->schedule) {
case EXPLORE:
afl->schedule = EXPLOIT;
break;
case EXPLOIT:
afl->schedule = MMOPT;
break;
case MMOPT:
afl->schedule = SEEK;
break;
case SEEK:
afl->schedule = EXPLORE;
break;
case FAST:
afl->schedule = COE;
break;
case COE:
afl->schedule = LIN;
break;
case LIN:
afl->schedule = QUAD;
break;
case QUAD:
afl->schedule = RARE;
break;
case RARE:
afl->schedule = FAST;
break;
}
// we must recalculate the scores of all queue entries
for (u32 i = 0; i < afl->queued_items; i++) {
if (likely(!afl->queue_buf[i]->disabled)) {
update_bitmap_score(afl, afl->queue_buf[i]);
}
}
}
prev_queued = afl->queued_items;
}
++runs_in_current_cycle;
do {
if (likely(!afl->old_seed_selection)) {
if (likely(afl->pending_favored && afl->smallest_favored >= 0)) {
afl->current_entry = afl->smallest_favored;
/*
} else {
for (s32 iter = afl->queued_items - 1; iter >= 0; --iter)
{
if (unlikely(afl->queue_buf[iter]->favored &&
!afl->queue_buf[iter]->was_fuzzed)) {
afl->current_entry = iter;
break;
}
}
*/
afl->queue_cur = afl->queue_buf[afl->current_entry];
} else {
if (unlikely(prev_queued_items < afl->queued_items ||
afl->reinit_table)) {
// we have new queue entries since the last run, recreate alias
// table
prev_queued_items = afl->queued_items;
create_alias_table(afl);
}
do {
afl->current_entry = select_next_queue_entry(afl);
} while (unlikely(afl->current_entry >= afl->queued_items));
afl->queue_cur = afl->queue_buf[afl->current_entry];
}
}
skipped_fuzz = fuzz_one(afl);
#ifdef INTROSPECTION
++afl->queue_cur->stats_selected;
if (unlikely(skipped_fuzz)) {
++afl->queue_cur->stats_skipped;
} else {
if (unlikely(afl->queued_items > stat_prev_queued_items)) {
afl->queue_cur->stats_finds +=
afl->queued_items - stat_prev_queued_items;
stat_prev_queued_items = afl->queued_items;
}
if (unlikely(afl->saved_crashes > prev_saved_crashes)) {
afl->queue_cur->stats_crashes +=
afl->saved_crashes - prev_saved_crashes;
prev_saved_crashes = afl->saved_crashes;
}
if (unlikely(afl->saved_tmouts > prev_saved_tmouts)) {
afl->queue_cur->stats_tmouts += afl->saved_tmouts - prev_saved_tmouts;
prev_saved_tmouts = afl->saved_tmouts;
}
}
#endif
if (unlikely(!afl->stop_soon && exit_1)) { afl->stop_soon = 2; }
if (unlikely(afl->old_seed_selection)) {
while (++afl->current_entry < afl->queued_items &&
afl->queue_buf[afl->current_entry]->disabled) {};
if (unlikely(afl->current_entry >= afl->queued_items ||
afl->queue_buf[afl->current_entry] == NULL ||
afl->queue_buf[afl->current_entry]->disabled)) {
afl->queue_cur = NULL;
} else {
afl->queue_cur = afl->queue_buf[afl->current_entry];
}
}
} while (skipped_fuzz && afl->queue_cur && !afl->stop_soon);
u64 cur_time = get_cur_time();
if (likely(afl->switch_fuzz_mode && afl->fuzz_mode == 0 &&
!afl->non_instrumented_mode) &&
unlikely(cur_time > (likely(afl->last_find_time) ? afl->last_find_time
: afl->start_time) +
afl->switch_fuzz_mode)) {
if (afl->afl_env.afl_no_ui) {
ACTF(
"No new coverage found for %llu seconds, switching to exploitation "
"strategy.",
afl->switch_fuzz_mode / 1000);
}
afl->fuzz_mode = 1;
}
if (likely(!afl->stop_soon && afl->sync_id)) {
if (unlikely(afl->is_main_node)) {
if (unlikely(cur_time > (afl->sync_time >> 1) + afl->last_sync_time)) {
if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) {
sync_fuzzers(afl);
}
}
} else {
if (unlikely(cur_time > afl->sync_time + afl->last_sync_time)) {
if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); }
}
}
}
}
stop_fuzzing:
afl->force_ui_update = 1; // ensure the screen is reprinted
afl->stop_soon = 1; // ensure everything is written
show_stats(afl); // print the screen one last time
write_bitmap(afl);
save_auto(afl);
#ifdef __AFL_CODE_COVERAGE
if (afl->fsrv.persistent_trace_bits) {
char cfn[4096];
snprintf(cfn, sizeof(cfn), "%s/covmap.dump", afl->out_dir);
FILE *cov_fd;
if ((cov_fd = fopen(cfn, "w")) == NULL) {
PFATAL("could not create '%s'", cfn);
}
// Write the real map size, as the map size must exactly match the pointer
// map in length.
fwrite(afl->fsrv.persistent_trace_bits, 1, afl->fsrv.real_map_size, cov_fd);
fclose(cov_fd);
}
#endif
if (afl->pizza_is_served) {
SAYF(CURSOR_SHOW cLRD "\n\n+++ Baking aborted %s +++\n" cRST,
afl->stop_soon == 2 ? "programmatically" : "by the chef");
} else {
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);
}
if (afl->not_on_tty) {
u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits);
u8 time_tmp[64];
u_stringify_time_diff(time_tmp, get_cur_time(), afl->start_time);
ACTF(
"Statistics: %u new corpus items found, %.02f%% coverage achieved, "
"%llu crashes saved, %llu timeouts saved, total runtime %s",
afl->queued_discovered,
((double)t_bytes * 100) / afl->fsrv.real_map_size, afl->saved_crashes,
afl->saved_hangs, time_tmp);
}
#ifdef PROFILING
SAYF(cYEL "[!] " cRST
"Profiling information: %llu ms total work, %llu ns/run\n",
time_spent_working / 1000000,
time_spent_working / afl->fsrv.total_execs);
#endif
if (afl->afl_env.afl_final_sync) {
SAYF(cYEL "[!] " cRST
"\nPerforming final sync, this make take some time ...\n");
sync_fuzzers(afl);
write_bitmap(afl);
SAYF(cYEL "[!] " cRST "Done!\n\n");
}
if (afl->is_main_node) {
u8 path[PATH_MAX];
sprintf(path, "%s/is_main_node", afl->out_dir);
unlink(path);
}
if (frida_afl_preload) { ck_free(frida_afl_preload); }
fclose(afl->fsrv.plot_file);
#ifdef INTROSPECTION
fclose(afl->fsrv.det_plot_file);
#endif
if (!afl->afl_env.afl_no_fastresume) {
/* create fastresume.bin */
u8 fr[PATH_MAX];
snprintf(fr, PATH_MAX, "%s/fastresume.bin", afl->out_dir);
ACTF("Writing %s ...", fr);
#ifdef HAVE_ZLIB
if ((fr_fd = ZLIBOPEN(fr, "wb9")) != NULL) {
#else
if ((fr_fd = open(fr, O_WRONLY | O_TRUNC | O_CREAT, DEFAULT_PERMISSION)) >=
0) {
#endif
u8 ver_string[8];
u32 w = 0;
u64 *ver = (u64 *)ver_string;
*ver = afl->shm.cmplog_mode + (sizeof(struct queue_entry) << 1);
ZLIBWRITE(fr_fd, ver_string, sizeof(ver_string), "ver_string");
ZLIBWRITE(fr_fd, afl->virgin_bits, afl->fsrv.map_size, "virgin_bits");
ZLIBWRITE(fr_fd, afl->virgin_tmout, afl->fsrv.map_size, "virgin_tmout");
ZLIBWRITE(fr_fd, afl->virgin_crash, afl->fsrv.map_size, "virgin_crash");
ZLIBWRITE(fr_fd, afl->var_bytes, afl->fsrv.map_size, "var_bytes");
w += sizeof(ver_string) + afl->fsrv.map_size * 4;
u8 on[1] = {1}, off[1] = {0};
u8 *o_start = (u8 *)&(afl->queue_buf[0]->colorized);
u8 *o_end = (u8 *)&(afl->queue_buf[0]->mother);
u32 q_len = o_end - o_start;
u32 m_len = (afl->fsrv.map_size >> 3);
struct queue_entry *q;
afl->pending_not_fuzzed = afl->queued_items;
afl->active_items = afl->queued_items;
for (u32 i = 0; i < afl->queued_items; i++) {
q = afl->queue_buf[i];
ZLIBWRITE(fr_fd, (u8 *)&(q->colorized), q_len, "queue data");
if (!q->trace_mini) {
ZLIBWRITE(fr_fd, off, 1, "no_mini");
w += q_len + 1;
} else {
ZLIBWRITE(fr_fd, on, 1, "yes_mini");
ZLIBWRITE(fr_fd, q->trace_mini, m_len, "trace_mini");
w += q_len + m_len + 1;
}
}
ZLIBCLOSE(fr_fd);
afl->var_byte_count = count_bytes(afl, afl->var_bytes);
OKF("Written fastresume.bin with %u bytes!", w);
} else {
WARNF("Could not create fastresume.bin");
}
}
destroy_queue(afl);
destroy_extras(afl);
destroy_custom_mutators(afl);
afl_shm_deinit(&afl->shm);
if (afl->shm_fuzz) {
afl_shm_deinit(afl->shm_fuzz);
ck_free(afl->shm_fuzz);
}
afl_fsrv_deinit(&afl->fsrv);
/* remove tmpfile */
if (!afl->in_place_resume && afl->fsrv.out_file) {
(void)unlink(afl->fsrv.out_file);
}
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);
if (afl->q_testcase_cache) { ck_free(afl->q_testcase_cache); }
afl_state_deinit(afl);
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 */