mcpField
A
aflpp

aflpp

github.com/trailofbits测试与质量7.9k5.2k 次安装今天更新
代码仓库

关于 aflpp

aflpp 技能指南介绍 AFL++ 模糊测试器,面向需要并行运行测试活动或多种变异策略的项目。内容包括安装、编译、语料管理、活动监控及结果分析。

主要功能

  1. 01LLVM、GCC 和 LTO 编译模式
  2. 02支持 libFuzzer 风格测试入口
  3. 03并行模糊测试活动
  4. 04语料库最小化与字典引导
  5. 05崩溃、覆盖率和活动状态分析

适用场景

  1. 01对生产代码库开展并行模糊测试
  2. 02在 libFuzzer 覆盖率停滞后继续测试
  3. 03在 CI 中设置自动停止条件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills --skill aflpp

安装后在 Claude Code 中输入/aflpp确认已加载。

附带文件agents/openai.yamlassets/trail-of-bits-mark.svg

详细介绍

AFL++

AFL++ 是从原始 AFL 模糊测试工具分支而来的版本,在保持稳定性的同时,提供了更出色的模糊测试性能和更高级的功能。与 libFuzzer 相比,AFL++ 的一大优势是稳定支持在多个核心上运行模糊测试活动,因此非常适合大规模模糊测试工作。

适用场景

模糊测试工具最适合的场景复杂度
AFL++多核模糊测试、多样化变异、成熟项目中
libFuzzer快速设置、单线程、简单的测试框架低
LibAFL自定义模糊测试工具、研究、高级用例高

以下情况建议选择 AFL++:

  • 需要多核模糊测试来最大化吞吐量
  • 项目可以使用 Clang 或 GCC 编译
  • 希望使用多样化的变异策略和成熟的工具链
  • libFuzzer 的覆盖率已停滞,需要进一步提升
  • 正在对可从并行执行中受益的生产代码库进行模糊测试

快速开始

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Call your code with fuzzer-provided data
    check_buf((char*)data, size);
    return 0;
}

编译并运行:

# Setup AFL++ wrapper script first (see Installation)
./afl++ docker afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz
mkdir seeds && echo "aaaa" > seeds/minimal_seed
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz

安装

AFL++ 有许多依赖项,包括 LLVM、Python 和 Rust。我们建议使用较新的 Debian 或 Ubuntu 发行版来运行 AFL++ 模糊测试。

方法适用场景支持的编译器
Ubuntu/Debian 软件源较新的 Ubuntu,仅支持基本功能Ubuntu 23.10:Clang 14 和 GCC 13
Debian 12:Clang 14 和 GCC 12
Docker(来自 Docker Hub)指定 AFL++ 版本、支持 Apple Silicon截至 4.35c:Clang 19 和 GCC 11
Docker(从源码构建)测试尚未发布的功能、应用补丁可在 Dockerfile 中配置
从源码构建避免使用 Docker、需要特定补丁可通过 LLVM_CONFIG 环境变量调整
Ubuntu/Debian

安装 afl++ 前,请使用 apt-cache show afl++ 检查软件包对 clang 版本的依赖,并安装匹配版本的 lld(例如 lld-17)。

apt install afl++ lld-17
Docker(来自 Docker Hub)
docker pull aflplusplus/aflplusplus:stable
Docker(从源码构建)
git clone --depth 1 --branch stable https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
docker build -t aflplusplus .
从源码构建

请参阅 Dockerfile,了解 Ubuntu 版本要求和依赖项。设置 LLVM_CONFIG 以指定 Clang 版本(例如 llvm-config-18)。

封装脚本设置

创建一个封装脚本,以便在主机或 Docker 上运行 AFL++:

cat <<'EOF' > ./afl++
#!/bin/sh
AFL_VERSION="${AFL_VERSION:-"stable"}"
case "$1" in
   host)
        shift
        bash -c "$*"
        ;;
    docker)
        shift
        /usr/bin/env docker run -i \
            --privileged \
            -v ./:/src \
            --rm \
            --name "afl_fuzzing_$$" \
            "aflplusplus/aflplusplus:$AFL_VERSION" \
            bash -c "cd /src && bash -c \"$*\""
        ;;
    *)
        echo "Usage: $0 {host|docker}"
        exit 1
        ;;
esac
EOF
chmod +x ./afl++

以下示例使用 docker 模式,但需要访问主机内核的系统配置命令除外。将 host 替换进去,即可在本机安装的 AFL++ 上运行其中任意命令。封装脚本会将模式参数之后的所有内容合并为一个 shell 字符串,因此引号不会保留:包含空格的参数(-x "my dict.dict")会被拆分为多个单词。请将此类文件重命名为不含空格的名称,或针对本次运行修改封装脚本。

这里有意省略了 -t。只要 stdin 不是终端,docker run -ti 就会因 the input device is not a TTY 而中止,CI 作业以及由 Agent 或脚本驱动的任何操作都属于这种情况。afl-fuzz 会检测到没有终端,并以普通状态行代替全屏 UI。像 watch 这样要求终端的程序必须在封装脚本的主机端运行。$$ 会展开为封装脚本的 PID,因此并行实例会使用不同的容器名称,而不会争用同一个 afl_fuzzing。docker ps 会截断 COMMAND 列,因此每一行看起来都一样;停止某个实例前,请使用 docker ps --no-trunc 区分各个实例。

安全警告: afl-system-config 和 afl-persistent-config 脚本需要 root 权限,并会禁用操作系统的安全功能。请勿在生产系统或开发环境中进行模糊测试,请改用专用 VM。

系统配置

每次重启后运行,可将每秒执行次数提升最多 15%:

./afl++ host afl-system-config

afl-system-config 会调整其运行所依赖的内核,因此请在托管测试活动的机器上运行。./afl++ docker afl-system-config 会通过包装器的 --privileged 容器应用相同设置;如果只通过 Docker 安装 AFL++,这是唯一的途径。

如需获得最高性能,请禁用内核安全缓解措施(需要 grub 引导加载程序,Docker 不支持):

./afl++ host afl-persistent-config
update-grub
reboot
./afl++ host afl-system-config

使用 cat /proc/cmdline 验证,输出中应包含 mitigations=off。

编写 Harness

Harness 结构

AFL++ 支持 libFuzzer 风格的 harness:

#include <stdint.h>
#include <stddef.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // 1. Validate input size if needed
    if (size < MIN_SIZE || size > MAX_SIZE) return 0;

    // 2. Call target function with fuzz data
    target_function(data, size);

    // 3. Return 0 (non-zero reserved for future use)
    return 0;
}
Harness 规则
应做不应做
在每次运行之间重置全局状态依赖之前运行留下的状态
妥善处理边缘情况遇到无效输入就退出
保持 harness 的确定性使用随机数生成器
释放已分配的内存造成内存泄漏
验证输入大小处理不受限制的输入

**另请参阅:**有关详细的 harness 编写技巧、处理复杂输入的模式, 以及高级策略,请参阅 fuzz-harness-writing 技术技能。

编译

AFL++ 提供多种编译模式,各有不同的权衡。

编译模式决策树

选择编译模式:

  • LTO 模式(afl-clang-lto):性能和插桩效果最佳。请优先尝试。
  • LLVM 模式(afl-clang-fast):LTO 编译失败时的备用选择。
  • GCC 插件(afl-gcc-fast):适用于需要 GCC 的项目。
基本编译(LLVM 模式)
./afl++ docker afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz
GCC 编译
./afl++ docker afl-g++-fast -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

**重要:**GCC 版本必须与用于编译 AFL++ GCC 插件的版本一致。

使用 Sanitizer
./afl++ docker AFL_USE_ASAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

**另请参阅:**有关 Sanitizer 的详细配置、常见问题和高级标志, 请参阅 address-sanitizer 和 undefined-behavior-sanitizer 技术技能。

构建标志

注意,-g 并非必需,AFL++ 编译器默认会添加该标志。

标志用途
-DNO_MAIN=1使用 libFuzzer harness 时跳过 main 函数
-O2生产环境优化级别(模糊测试推荐)
-fsanitize=fuzzer启用 libFuzzer 兼容模式,并在链接可执行文件时添加模糊测试运行时
-fsanitize=fuzzer-no-link进行插桩,但不链接模糊测试运行时(适用于静态库和目标文件)

Corpus 管理

创建初始 Corpus

AFL++ 至少需要一个非空种子文件:

mkdir seeds
echo "aaaa" > seeds/minimal_seed

对于实际项目,请收集有代表性的输入:

  • 下载你要进行模糊测试的格式的示例文件
  • 从项目的测试套件中提取测试用例
  • 为你的文件格式使用最小的有效输入
Corpus 最小化

测试活动结束后,精简 corpus,仅保留覆盖范围不同的内容:

./afl++ docker afl-cmin -i out/default/queue -o minimized_corpus -- ./fuzz

**另请参阅:**有关 corpus 创建策略、字典和种子选择, 请参阅 fuzzing-corpus 技术技能。

运行测试活动

基本运行
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz
设置环境变量
./afl++ docker AFL_FAST_CAL=1 afl-fuzz -i seeds -o out -- ./fuzz
解读输出

无论采用哪种模式,AFL++ 都会报告这些统计数据,但解读方式取决于所用模式。包装器的 docker run -i 不会为容器分配 TTY,因此 afl-fuzz 会退出全屏 UI,改为在日志中写入纯文本状态行。下方字段会显示在日志和 state/<instance>/fuzzer_stats 中。若要使用交互式 UI,请在终端中运行 host 模式,或在手动监控的运行中为包装器添加 -t。

输出含义
execs/sec执行速度,越高越好
cycles done已完成的队列遍历次数
corpus count队列中不同测试用例的数量
saved crashes发现的不同崩溃数量
stability稳定边的百分比(应接近 100%)
输出目录结构
out/default/
├── cmdline          # How was the SUT invoked?
├── crashes/         # Inputs that crash the SUT
│   └── id:000000,sig:06,src:000002,time:286,execs:13105,op:havoc,rep:4
├── hangs/           # Inputs that hang the SUT
├── queue/           # Test cases reproducing final fuzzer state
│   ├── id:000000,time:0,execs:0,orig:minimal_seed
│   └── id:000001,src:000000,time:0,execs:8,op:havoc,rep:6,+cov
├── fuzzer_stats     # Campaign statistics
└── plot_data        # Data for plotting
分析结果

查看实时活动统计信息:

./afl++ docker afl-whatsup out

创建覆盖率图表。aflplusplus 镜像已自带 gnuplot-nox;在主机模式下,请先使用 apt install gnuplot 安装 gnuplot。

./afl++ docker afl-plot out/default out_graph/
重新执行测试用例

传入 out/default/crashes/ 中的一个文件名:

./afl++ docker ./fuzz out/default/crashes/id:000000,sig:06,src:000002,time:286,execs:13105,op:havoc,rep:4
Fuzzer 选项
选项用途
-G 4000测试输入的最大长度(默认值:1048576 字节)
-t 1000每个测试用例的超时时间,单位为毫秒(默认值:1000ms)
-m 1000内存限制,单位为兆字节(默认值:0 = 不限)
-x ./dict.dict使用字典文件来引导变异

重要环境变量

AFL++ 有许多环境变量,但大多数只适用于少数场景。以下是实际使用中重要的变量。

始终设置这些变量
# Every campaign should use tmpfs — SSDs will thank you, and it's faster
AFL_TMPDIR=/dev/shm

设置 AFL_TMPDIR 可免费提升性能,且没有任何缺点——不设置它会损耗 SSD 并降低模糊测试速度。

慢速目标
# Speeds up calibration ~2.5x — use when targets are slow (e.g., >10 ms/exec)
AFL_FAST_CAL=1

AFL_FAST_CAL 可在几乎不损失精度的情况下缩短校准时间。特别推荐用于慢速目标,因为这类目标的校准通常会耗费很长时间。

多核活动
# On the primary (-M) instance only — needed for afl-cmin, not for fuzzing itself
AFL_FINAL_SYNC=1

# On all instances — cache test cases in memory (default: 50 MB, good range: 50-250 MB)
AFL_TESTCACHE_SIZE=100

AFL_FINAL_SYNC 会指示主实例在停止时从所有次要实例执行最终导入。这不会影响模糊测试过程本身——只有在之后运行 afl-cmin 进行语料库最小化时才有意义,可确保主实例的队列包含完整的合并语料库。AFL_TESTCACHE_SIZE 会在内存中缓存测试用例,以减少磁盘 I/O;默认值为 50 MB,对于大多数活动而言,50-250 MB 的取值效果良好。

CI/自动化模糊测试
# Fail fast if fuzzing isn't finding anything
AFL_EXIT_ON_TIME=3600  # 1 hour with no new paths = stop

# Or run until "done" (all queue entries processed)
AFL_EXIT_WHEN_DONE=1

# Headless environments
AFL_NO_UI=1

在 CI 中进行不设限的模糊测试会浪费资源。请设置时间限制或使用退出条件。

应避免的变量
变量不使用的原因
AFL_NO_ARITH可能降低二进制格式的覆盖率,但对基于文本的目标可能有用
AFL_SHUFFLE_QUEUE仅适用于特殊配置,通常弊大于利
AFL_DISABLE_TRIM修剪很有价值,不要无故禁用

多核模糊测试

AFL++ 在多核模糊测试方面表现出色,主要有两个优势:

  1. 每秒执行次数更多(随物理核心数线性增长)
  2. 非对称模糊测试(例如,一个 ASan 作业,其余不使用检测器)
启动活动

启动主 Fuzzer(在后台运行):

./afl++ docker afl-fuzz -M primary -i seeds -o state -- ./fuzz 1>primary.log 2>primary.error </dev/null &

启动次要 Fuzzer(数量与核心数相同):

./afl++ docker afl-fuzz -S secondary01 -i seeds -o state -- ./fuzz 1>secondary01.log 2>secondary01.error </dev/null &
./afl++ docker afl-fuzz -S secondary02 -i seeds -o state -- ./fuzz 1>secondary02.log 2>secondary02.error </dev/null &

</dev/null 是必需的,并非装饰。docker run -i 会让客户端继续读取自身的标准输入,而读取终端的后台进程会收到 SIGTTIN,其默认操作是停止进程——因此,不进行重定向时,这些作业会在 jobs 中显示为 Stopped,且永远不会执行模糊测试。

监控多核活动

列出所有正在运行的作业:

jobs

查看实时统计信息。watch 需要终端,而 docker run -i 不会提供终端,因此应将整个调用封装起来,而不是在容器内运行 watch。每次刷新都会启动一个容器,这就是刷新间隔设为 5 秒而非 1 秒的原因:

watch -n5 --color ./afl++ docker afl-whatsup state/
停止所有 Fuzzer
kill $(jobs -p)

覆盖率分析

AFL++ 会通过边插桩自动跟踪覆盖率。覆盖率信息存储在 fuzzer_stats 和 plot_data 中。

测量覆盖率

使用 afl-plot 可视化覆盖率随时间的变化:

./afl++ docker afl-plot out/default out_graph/
提高覆盖率
  • 使用字典进行格式感知的模糊测试
  • 延长活动运行时间(cycles_wo_finds 表示是否已进入平台期)
  • 使用多核模糊测试尝试不同的变异策略
  • 分析覆盖率缺口并添加有针对性的种子输入

另请参阅: 有关详细的覆盖率分析技巧、如何识别覆盖率缺口以及系统性提高覆盖率的方法,请参阅 coverage-analysis 技术技能。

CMPLOG

CMPLOG/RedQueen 是所有模糊测试器中最好的路径约束求解机制。 要启用它,需要为模糊测试目标进行相应插桩。 构建模糊测试目标之前,设置环境变量:

./afl++ docker AFL_LLVM_CMPLOG=1 make

编译和链接测试框架无需特别操作。

要使用经过 CMPLOG 插桩的模糊测试目标运行模糊测试器实例,请在类似命令的参数中添加 -c0:

./afl++ docker afl-fuzz -c0 -S cmplog -i seeds -o state -- ./fuzz 1>cmplog.log 2>cmplog.error </dev/null &

Sanitizer 集成

Sanitizer 对于发现不会立即导致崩溃的内存损坏错误至关重要。

AddressSanitizer(ASan)
./afl++ docker AFL_USE_ASAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

**注意:**由于需要预留 20TB 虚拟内存,ASan 不支持内存限制(-m)。

UndefinedBehaviorSanitizer(UBSan)
./afl++ docker AFL_USE_UBSAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer,undefined harness.cc main.cc -o fuzz
常见 Sanitizer 问题
问题解决方案
ASan 会拖慢模糊测试在多核配置中仅使用 1 个 ASan 作业
栈耗尽使用 ASAN_OPTIONS=stack_size=... 增大栈
GCC 版本不匹配确保系统 GCC 与 AFL++ 插件版本一致

**另请参阅:**有关全面的 sanitizer 配置和故障排除, 请参阅 address-sanitizer 技术技能。

高级用法

提示和技巧
提示帮助原因
尽可能使用 LLVMFuzzerTestOneInput 测试框架如果模糊测试活动的稳定性至少达到 85%,这是最高效的模糊测试方式。如果达不到,请尝试标准输入或文件输入模糊测试
使用字典帮助模糊测试器发现特定格式的关键词和魔数字节
设置合理的超时时间防止系统负载导致误报
限制输入大小更大的输入不一定能探索更多空间
监控稳定性稳定性低表明存在非确定性行为
标准输入模糊测试

AFL++ 可以对从 stdin 读取输入且未使用 libFuzzer 测试框架的程序进行模糊测试:

./afl++ docker afl-clang-fast++ -O2 main_stdin.c -o fuzz_stdin
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz_stdin

这种方式比持久模式慢,但不需要编写测试框架代码。

文件输入模糊测试

对于读取文件的程序,请使用 @@ 占位符:

./afl++ docker afl-clang-fast++ -O2 main_file.c -o fuzz_file
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz_file @@

为提高性能,可以使用 fmemopen 从内存创建文件描述符。

参数模糊测试

使用 argv-fuzz-inl.h 对命令行参数进行模糊测试:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __AFL_COMPILER
#include "argv-fuzz-inl.h"
#endif

void check_buf(char *buf, size_t buf_len) {
    if(buf_len > 0 && buf[0] == 'a') {
        if(buf_len > 1 && buf[1] == 'b') {
            if(buf_len > 2 && buf[2] == 'c') {
                abort();
            }
        }
    }
}

int main(int argc, char *argv[]) {
#ifdef __AFL_COMPILER
    AFL_INIT_ARGV();
#endif

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <input_string>\n", argv[0]);
        return 1;
    }

    char *input_buf = argv[1];
    size_t len = strlen(input_buf);
    check_buf(input_buf, len);
    return 0;
}

下载头文件:

curl -O https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/utils/argv_fuzzing/argv-fuzz-inl.h

编译并运行:

./afl++ docker afl-clang-fast++ -O2 main_arg.c -o fuzz_arg
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz_arg
性能调优
设置影响
CPU 核心数随物理核心数线性扩展
持久模式比 fork 服务器快 10-20 倍
-G 输入大小限制越小速度越快,但可能漏掉错误
ASan 比例每 4-8 个非 ASan 作业配置 1 个 ASan 作业

故障排除

问题原因解决方案
执行速度低(<1k 次/秒)未使用持久模式创建 LLVMFuzzerTestOneInput 风格的测试框架
稳定性低(<85%)存在非确定性代码通过 stdin 或文件输入对程序进行模糊测试,或创建相应的测试框架
GCC 插件错误GCC 版本不匹配确保系统 GCC 与 AFL++ 构建版本一致,并安装 gcc-$GCC_VERSION-plugin-dev
未发现崩溃需要 Sanitizer使用 AFL_USE_ASAN=1 重新编译
超出内存限制ASan 使用 20TB 虚拟内存使用 ASan 时移除 -m 标志
Docker 性能下降虚拟化开销在生产环境模糊测试中使用裸机或虚拟机

相关技能

技术技能
技能使用场景
fuzz-harness-writing编写有效测试框架的详细指南
address-sanitizer在模糊测试期间检测内存错误
undefined-behavior-sanitizer检测未定义行为错误
fuzzing-corpus构建和管理种子语料库
fuzzing-dictionaries创建用于格式感知模糊测试的字典
相关模糊测试器
技能适用情况
libfuzzer快速原型开发,单线程模糊测试已足够
libafl需要自定义变异器或研究级功能

资源

重要外部资源

AFL++ GitHub 仓库 官方仓库,提供全面的文档、示例和问题跟踪器。

深入了解模糊测试 AFL++ 团队编写的进阶文档,介绍插桩模式、优化技巧和高级用例。

AFL++ 内部原理 深入介绍 AFL++ 的内部实现、变异策略和覆盖率跟踪机制。

AFL++:整合模糊测试研究的渐进成果 介绍 AFL++ 架构及其相较于原版 AFL 的性能提升的研究论文。

视频资源

评论

支持 Markdown