libfuzzer by trailofbits/skills
npx skills add https://github.com/trailofbits/skills --skill libfuzzerlibFuzzer 是 LLVM 项目的一部分,是一个进程内、覆盖率引导的模糊测试工具。由于其简单性和与 LLVM 工具链的集成,它是模糊测试 C/C++ 项目的推荐起点。虽然 libFuzzer 自 2022 年底以来一直处于仅维护模式,但它比替代方案更容易安装和使用,拥有广泛的支持,并且在可预见的未来将继续得到维护。
| 模糊测试工具 | 最适合 | 复杂度 |
|---|---|---|
| libFuzzer | 快速设置,单项目模糊测试 | 低 |
| AFL++ | 多核模糊测试,多样化变异 | 中 |
| LibAFL | 自定义模糊测试工具,研究项目 | 高 |
| Honggfuzz | 基于硬件的覆盖率 | 中 |
在以下情况选择 libFuzzer:
注意: 为 libFuzzer 编写的测试套件与 AFL++ 兼容,如果你需要更高级的功能(如更好的多核支持),可以轻松过渡。
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Validate input if needed
if (size < 1) return 0;
// Call your target function with fuzzer-provided data
my_target_function(data, size);
return 0;
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
编译并运行:
clang++ -fsanitize=fuzzer,address -g -O2 harness.cc target.cc -o fuzz
mkdir corpus/
./fuzz corpus/
apt install clang llvm
对于最新的 LLVM 版本:
# 从 apt.llvm.org 添加 LLVM 仓库
# 然后安装特定版本,例如:
apt install clang-18 llvm-18
# 使用 Homebrew
brew install llvm
# 或使用 Nix
nix-env -i clang
通过 Visual Studio 安装 Clang。有关设置说明,请参考 Microsoft 的文档。
建议: 如果可能,在本地 x86_64 虚拟机上进行模糊测试,或在 DigitalOcean、AWS 或 Hetzner 上租用一台。Linux 为 libFuzzer 提供了最好的支持。
clang++ --version
# 应显示 LLVM 版本信息
测试套件是模糊测试工具的入口点。libFuzzer 会使用不同的输入反复调用 LLVMFuzzerTestOneInput 函数。
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// 1. 可选:验证输入大小
if (size < MIN_REQUIRED_SIZE) {
return 0; // 拒绝太小的输入
}
// 2. 可选:将原始字节转换为结构化数据
// 示例:从字节数组中解析两个整数
if (size >= 2 * sizeof(uint32_t)) {
uint32_t a = *(uint32_t*)(data);
uint32_t b = *(uint32_t*)(data + sizeof(uint32_t));
my_function(a, b);
}
// 3. 调用目标函数
target_function(data, size);
// 4. 始终返回 0(非零值保留供将来使用)
return 0;
}
| 应做 | 不应做 |
|---|---|
| 处理所有输入类型(空、巨大、格式错误) | 调用 exit() - 会停止模糊测试进程 |
| 返回前等待所有线程结束 | 让线程继续运行 |
| 保持测试套件快速且简单 | 添加过多的日志记录或复杂性 |
| 保持确定性 | 使用随机数生成器或读取 /dev/random |
| 在每次运行之间重置全局状态 | 依赖之前执行的状态 |
| 使用狭窄、聚焦的目标 | 在一个测试套件中混合不相关的数据格式(PNG + TCP) |
原理:
对于复杂输入(字符串、多个参数),使用 FuzzedDataProvider 辅助工具:
#include <stdint.h>
#include <stddef.h>
#include "FuzzedDataProvider.h" // 来自 LLVM 项目
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
// 提取结构化数据
size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();
std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
// 使用提取的数据调用目标函数
char* result = concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
if (result != NULL) {
free(result);
}
return 0;
}
从 LLVM 仓库 下载 FuzzedDataProvider.h。
使用单个测试套件测试多个相关函数:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1 + 2 * sizeof(int32_t)) {
return 0;
}
uint8_t mode = data[0];
int32_t numbers[2];
memcpy(numbers, data + 1, 2 * sizeof(int32_t));
// 根据第一个字节选择函数
switch (mode % 4) {
case 0: add(numbers[0], numbers[1]); break;
case 1: subtract(numbers[0], numbers[1]); break;
case 2: multiply(numbers[0], numbers[1]); break;
case 3: divide(numbers[0], numbers[1]); break;
}
return 0;
}
另请参阅: 有关详细的测试套件编写技术、处理复杂输入的模式、结构感知模糊测试和基于 protobuf 的模糊测试,请参阅 fuzz-harness-writing 技术技能。
关键标志是 -fsanitize=fuzzer,它:
链接 libFuzzer 运行时(提供 main 函数)
启用 SanitizerCoverage 插桩以进行覆盖率跟踪
禁用内置函数,如 memcmp
clang++ -fsanitize=fuzzer -g -O2 harness.cc target.cc -o fuzz
标志解释:
-fsanitize=fuzzer: 启用 libFuzzer-g: 添加调试符号(有助于崩溃分析)-O2: 生产级优化(推荐用于模糊测试)-DNO_MAIN: 如果你的代码有 main 函数,则定义此宏AddressSanitizer(推荐):
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzz
多个 sanitizer:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzz
另请参阅: 有关详细的 sanitizer 配置、常见问题、ASAN_OPTIONS 标志和高级 sanitizer 用法,请参阅 address-sanitizer 和 undefined-behavior-sanitizer 技术技能。
| 标志 | 用途 |
|---|---|
-fsanitize=fuzzer | 启用 libFuzzer 运行时和插桩 |
-fsanitize=address | 启用 AddressSanitizer(内存错误检测) |
-fsanitize=undefined | 启用 UndefinedBehaviorSanitizer |
-fsanitize=fuzzer-no-link | 插桩但不链接模糊测试工具(用于库) |
-g | 包含调试符号 |
-O2 | 生产优化级别 |
-U_FORTIFY_SOURCE | 禁用强化(可能会干扰 ASan) |
对于生成静态库的项目:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
make
2. 将静态库与你的测试套件链接:
clang++ -fsanitize=fuzzer -fsanitize=address harness.cc libmylib.a -o fuzz
project(FuzzTarget)
cmake_minimum_required(VERSION 3.0)
add_executable(fuzz main.cc harness.cc)
target_compile_definitions(fuzz PRIVATE NO_MAIN=1)
target_compile_options(fuzz PRIVATE -g -O2 -fsanitize=fuzzer -fsanitize=address)
target_link_libraries(fuzz -fsanitize=fuzzer -fsanitize=address)
构建:
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build .
为语料库创建一个目录(可以从空目录开始):
mkdir corpus/
可选但推荐: 提供种子输入(有效的示例文件):
# 对于 PNG 解析器:
cp examples/*.png corpus/
# 对于协议解析器:
cp test_packets/*.bin corpus/
种子输入的好处:
语料库目录包含:
a9993e364706816aba3e25717850c26c9cd0d89d)libFuzzer 在模糊测试期间自动最小化语料库条目。要显式最小化:
mkdir minimized_corpus/
./fuzz -merge=1 minimized_corpus/ corpus/
这将在 minimized_corpus/ 中创建一个去重、最小化的语料库。
另请参阅: 有关语料库创建策略、种子选择、特定格式的语料库构建和语料库维护,请参阅 fuzzing-corpus 技术技能。
./fuzz corpus/
这将一直运行直到发现崩溃或你停止它(Ctrl+C)。
./fuzz -fork=1 -ignore_crashes=1 corpus/
-fork 和 -ignore_crashes 标志(实验性但广泛使用)允许在发现崩溃后继续模糊测试。
控制输入大小:
./fuzz -max_len=4000 corpus/
经验法则:最小现实输入大小的 2 倍。
设置超时:
./fuzz -timeout=2 corpus/
中止运行时间超过 2 秒的测试用例。
使用字典:
./fuzz -dict=./format.dict corpus/
关闭 stdout/stderr(加速模糊测试):
./fuzz -close_fd_mask=3 corpus/
查看所有选项:
./fuzz -help=1
选项 1:作业和工作进程(推荐):
./fuzz -jobs=4 -workers=4 -fork=1 -ignore_crashes=1 corpus/
-jobs=4: 运行 4 个顺序测试活动-workers=4: 使用 4 个进程并行处理作业选项 2:分叉模式:
./fuzz -fork=4 -ignore_crashes=1 corpus/
注意: 对于严肃的多核模糊测试,考虑切换到 AFL++、Honggfuzz 或 LibAFL。
重新运行单个崩溃:
./fuzz ./crash-a9993e364706816aba3e25717850c26c9cd0d89d
测试目录中的所有输入而不进行模糊测试:
./fuzz -runs=0 corpus/
当模糊测试运行时,你会看到类似以下的统计信息:
INFO: Seed: 3517090860
INFO: Loaded 1 modules (9 inline 8-bit counters)
#2 INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 26Mb
#57 NEW cov: 4 ft: 5 corp: 2/4b lim: 4 exec/s: 0 rss: 26Mb
| 输出 | 含义 |
|---|---|
INITED | 模糊测试已初始化 |
NEW | 发现新的覆盖率,已添加到语料库 |
REDUCE | 输入在保持覆盖率的同时被最小化 |
cov: N | 命中的覆盖率边数 |
corp: X/Yb | 语料库大小:X 个条目,Y 总字节数 |
exec/s: N | 每秒执行次数 |
rss: NMb | 常驻内存使用量 |
崩溃时:
==11672== ERROR: libFuzzer: deadly signal
artifact_prefix='./'; Test unit written to ./crash-a9993e364706816aba3e25717850c26c9cd0d89d
0x61,0x62,0x63,
abc
Base64: YWJj
崩溃被保存到 ./crash-<哈希>,输入以十六进制、UTF-8 和 Base64 格式显示。
可重现性: 使用 -seed=<值> 来重现模糊测试活动(仅限单核)。
字典通过提供有关输入格式的提示,帮助模糊测试工具更快地发现有趣的输入。
创建一个包含带引号字符串的文本文件(每行一个):
# 以 '#' 开头的行是注释
# 魔术字节
magic="\x89PNG"
magic2="IEND"
# 关键词
"GET"
"POST"
"Content-Type"
# 十六进制序列
delimiter="\xFF\xD8\xFF"
./fuzz -dict=./format.dict corpus/
从头文件生成:
grep -o '".*"' header.h > header.dict
从手册页生成:
man curl | grep -oP '^\s*(--|-)\K\S+' | sed 's/[,.]$//' | sed 's/^/"&/; s/$/&"/' | sort -u > man.dict
从二进制字符串生成:
strings ./binary | sed 's/^/"&/; s/$/&"/' > strings.dict
使用 LLM: 要求 ChatGPT 或类似工具为你的格式生成字典(例如,“为 JSON 解析器生成一个 libFuzzer 字典”)。
另请参阅: 有关高级字典生成、特定格式字典和字典优化策略,请参阅 fuzzing-dictionaries 技术技能。
虽然 libFuzzer 显示基本的覆盖率统计信息(cov: N),但详细的覆盖率分析需要额外的工具。
1. 使用覆盖率插桩重新编译:
clang++ -fsanitize=fuzzer -fprofile-instr-generate -fcoverage-mapping harness.cc target.cc -o fuzz
2. 运行模糊测试工具以收集覆盖率:
LLVM_PROFILE_FILE="coverage-%p.profraw" ./fuzz -runs=10000 corpus/
3. 合并覆盖率数据:
llvm-profdata merge -sparse coverage-*.profraw -o coverage.profdata
4. 生成覆盖率报告:
llvm-cov show ./fuzz -instr-profile=coverage.profdata
5. 生成 HTML 报告:
llvm-cov show ./fuzz -instr-profile=coverage.profdata -format=html > coverage.html
技巧:
另请参阅: 有关详细的覆盖率分析技术、识别覆盖率差距、系统性覆盖率改进以及比较不同模糊测试工具的覆盖率,请参阅 coverage-analysis 技术技能。
ASan 检测内存错误,如缓冲区溢出和释放后使用错误。强烈推荐用于模糊测试。
启用 ASan:
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzz
ASan 输出示例:
==1276163==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000c4ab1
WRITE of size 1 at 0x6020000c4ab1 thread T0
#0 0x55555568631a in check_buf(char*, unsigned long) main.cc:13:25
#1 0x5555556860bf in LLVMFuzzerTestOneInput harness.cc:7:3
使用环境变量配置 ASan:
ASAN_OPTIONS=verbosity=1:abort_on_error=1 ./fuzz corpus/
重要标志:
verbosity=1: 显示 ASan 处于活动状态detect_leaks=0: 禁用泄漏检测(泄漏在最后报告)abort_on_error=1: 出错时调用 abort() 而不是 _exit()缺点:
-rss_limit_mb=0)另请参阅: 有关全面的 ASan 配置、常见陷阱、符号化和与其他 sanitizer 结合使用,请参阅 address-sanitizer 技术技能。
UBSan 检测未定义行为,如整数溢出、空指针解引用等。
启用 UBSan:
clang++ -fsanitize=fuzzer,undefined -g -O2 harness.cc target.cc -o fuzz
与 ASan 结合使用:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzz
MSan 检测未初始化的内存读取。使用更复杂(需要重新构建所有依赖项)。
clang++ -fsanitize=fuzzer,memory -g -O2 harness.cc target.cc -o fuzz
| 问题 | 解决方案 |
|---|---|
| ASan 使模糊测试太慢 | 对非致命错误使用 -fsanitize-recover=address |
| 内存不足 | 设置 ASAN_OPTIONS=rss_limit_mb=0 或 -rss_limit_mb=0 |
| 堆栈耗尽 | 增加堆栈大小:ASAN_OPTIONS=stack_size=8388608 |
与 _FORTIFY_SOURCE 的误报 | 使用 -U_FORTIFY_SOURCE 标志 |
| 依赖项中的 MSan 报告 | 使用 -fsanitize=memory 重新构建所有依赖项 |
libpng 是一个广泛使用的用于读写 PNG 图像的库。错误可能导致安全问题。
1. 获取源代码:
curl -L -O https://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar xf libpng-1.6.37.tar.xz
cd libpng-1.6.37/
2. 安装依赖项:
apt install zlib1g-dev
3. 使用模糊测试插桩编译:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
make
4. 获取测试套件(或自己编写):
curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc
5. 准备语料库和字典:
mkdir corpus/
curl -o corpus/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png
curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict
6. 链接并编译模糊测试工具:
clang++ -fsanitize=fuzzer -fsanitize=address libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz
7. 运行模糊测试活动:
./fuzz -close_fd_mask=3 -dict=./png.dict corpus/
发现除零错误的测试套件:
#include <stdint.h>
#include <stddef.h>
double divide(uint32_t numerator, uint32_t denominator) {
// Bug: No check if denominator is zero
return numerator / denominator;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if(size != 2 * sizeof(uint32_t)) {
return 0;
}
uint32_t numerator = *(uint32_t*)(data);
uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));
divide(numerator, denominator);
return 0;
}
编译并模糊测试:
clang++ -fsanitize=fuzzer harness.cc -o fuzz
./fuzz
模糊测试工具将快速找到导致崩溃的输入。
| 技巧 | 为何有帮助 |
|---|---|
| 从单核开始,切换到 AFL++ 进行多核测试 | libFuzzer 测试套件适用于 AFL++ |
| 对结构化格式使用字典 | 快 10-100 倍发现错误 |
使用 -close_fd_mask=3 关闭文件描述符 | 如果被测系统写入输出,可以加速 |
设置合理的 -max_len | 防止在巨大输入上浪费时间 |
| 运行数天/数周,而不是数分钟 | 覆盖率平台期需要时间来突破 |
| 使用测试套件中的种子语料库 | 从有效输入开始模糊测试 |
对于高度结构化的输入(例如,复杂协议、文件格式),使用 libprotobuf-mutator:
有关详细信息,请参阅 结构感知模糊测试文档。
libFuzzer 允许自定义变异器进行专门的模糊测试:
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed) {
// Custom mutation logic
return new_size;
}
extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize,
unsigned int Seed) {
// Custom crossover logic
return new_size;
}
| 设置 | 影响 |
|---|---|
-close_fd_mask=3 | 关闭 stdout/stderr,加速模糊测试 |
-max_len=<合理大小> | 避免在巨大输入上浪费时间 |
-timeout=<秒数> | 检测挂起,防止执行卡住 |
| 禁用 ASan 作为基准 | 加速 2-4 倍(但会错过内存错误) |
使用 -jobs 和 -workers | 有限的多核支持 |
| 在 Linux 上运行 | 最佳平台支持和性能 |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 数小时后未发现崩溃 | 语料库质量差,覆盖率低 | 添加种子输入,使用字典,检查测试套件 |
| 执行速度非常慢(<100 次/秒) | 目标太复杂,日志记录过多 | 优化目标,使用 -close_fd_mask=3,减少日志记录 |
| 内存不足 | ASan 的 20TB 虚拟内存 | 设置 -rss_limit_mb=0 以禁用 RSS 限制 |
| 模糊测试工具在第一次崩溃后停止 | 默认行为 | 使用 -fork=1 -ignore_crashes=1 以继续运行 |
| 无法重现崩溃 | 测试套件/目标中的非确定性 | 移除随机数生成,全局状态 |
使用 -fsanitize=fuzzer 链接错误 | 缺少 libFuzzer 运行时 | 确保使用 Clang,检查 LLVM 安装 |
| GCC 项目无法用 Clang 编译 | GCC 特定代码 | 切换到使用 gcc_plugin 的 AFL++ |
| 覆盖率没有提高 | 语料库平台期 | 运行更长时间,添加字典,改进种子,检查覆盖率报告 |
| 崩溃但 ASan 未触发 | 没有 ASan 无法检测到内存错误 | 使用 -fsanitize=address 重新编译 |
| 技能 | 使用场景 |
|---|---|
| fuzz-harness-writing | 编写有效测试套件的详细指导、结构感知模糊测试和 FuzzedDataProvider 用法 |
| address-sanitizer | 内存错误检测配置、ASAN_OPTIONS 和故障排除 |
| undefined-behavior-sanitizer | 在模糊测试期间检测未定义行为 |
| coverage-analysis | 测量模糊测试有效性和识别未测试代码路径 |
| fuzzing-corpus | 构建和管理种子语料库、语料库最小化策略 |
| fuzzing-dictionaries | 创建特定格式字典以更快发现错误 |
| 技能 | 何时考虑 |
|---|---|
| aflpp | 当你需要严肃的多核模糊测试,或当 libFuzzer 覆盖率进入平台期时 |
| honggfuzz | 当你在 Linux 上想要基于硬件的覆盖率反馈时 |
| libafl | 当你构建自定义模糊测试工具或进行模糊测试研究时 |
每周安装次数
1.1K
仓库
GitHub 星标数
3.9K
首次出现
2026年1月19日
安全审计
安装于
claude-code945
opencode904
gemini-cli885
codex880
cursor857
github-copilot830
libFuzzer is an in-process, coverage-guided fuzzer that is part of the LLVM project. It's the recommended starting point for fuzzing C/C++ projects due to its simplicity and integration with the LLVM toolchain. While libFuzzer has been in maintenance-only mode since late 2022, it is easier to install and use than its alternatives, has wide support, and will be maintained for the foreseeable future.
| Fuzzer | Best For | Complexity |
|---|---|---|
| libFuzzer | Quick setup, single-project fuzzing | Low |
| AFL++ | Multi-core fuzzing, diverse mutations | Medium |
| LibAFL | Custom fuzzers, research projects | High |
| Honggfuzz | Hardware-based coverage | Medium |
Choose libFuzzer when:
Note: Fuzzing harnesses written for libFuzzer are compatible with AFL++, making it easy to transition if you need more advanced features like better multi-core support.
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Validate input if needed
if (size < 1) return 0;
// Call your target function with fuzzer-provided data
my_target_function(data, size);
return 0;
}
Compile and run:
clang++ -fsanitize=fuzzer,address -g -O2 harness.cc target.cc -o fuzz
mkdir corpus/
./fuzz corpus/
apt install clang llvm
For the latest LLVM version:
# Add LLVM repository from apt.llvm.org
# Then install specific version, e.g.:
apt install clang-18 llvm-18
# Using Homebrew
brew install llvm
# Or using Nix
nix-env -i clang
Install Clang through Visual Studio. Refer to Microsoft's documentation for setup instructions.
Recommendation: If possible, fuzz on a local x86_64 VM or rent one on DigitalOcean, AWS, or Hetzner. Linux provides the best support for libFuzzer.
clang++ --version
# Should show LLVM version information
The harness is the entry point for the fuzzer. libFuzzer calls the LLVMFuzzerTestOneInput function repeatedly with different inputs.
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// 1. Optional: Validate input size
if (size < MIN_REQUIRED_SIZE) {
return 0; // Reject inputs that are too small
}
// 2. Optional: Convert raw bytes to structured data
// Example: Parse two integers from byte array
if (size >= 2 * sizeof(uint32_t)) {
uint32_t a = *(uint32_t*)(data);
uint32_t b = *(uint32_t*)(data + sizeof(uint32_t));
my_function(a, b);
}
// 3. Call target function
target_function(data, size);
// 4. Always return 0 (non-zero reserved for future use)
return 0;
}
| Do | Don't |
|---|---|
| Handle all input types (empty, huge, malformed) | Call exit() - stops fuzzing process |
| Join all threads before returning | Leave threads running |
| Keep harness fast and simple | Add excessive logging or complexity |
| Maintain determinism | Use random number generators or read /dev/random |
| Reset global state between runs | Rely on state from previous executions |
| Use narrow, focused targets | Mix unrelated data formats (PNG + TCP) in one harness |
Rationale:
For complex inputs (strings, multiple parameters), use the FuzzedDataProvider helper:
#include <stdint.h>
#include <stddef.h>
#include "FuzzedDataProvider.h" // From LLVM project
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
// Extract structured data
size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();
std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
// Call target with extracted data
char* result = concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
if (result != NULL) {
free(result);
}
return 0;
}
Download FuzzedDataProvider.h from the LLVM repository.
Use a single harness to test multiple related functions:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1 + 2 * sizeof(int32_t)) {
return 0;
}
uint8_t mode = data[0];
int32_t numbers[2];
memcpy(numbers, data + 1, 2 * sizeof(int32_t));
// Select function based on first byte
switch (mode % 4) {
case 0: add(numbers[0], numbers[1]); break;
case 1: subtract(numbers[0], numbers[1]); break;
case 2: multiply(numbers[0], numbers[1]); break;
case 3: divide(numbers[0], numbers[1]); break;
}
return 0;
}
See Also: For detailed harness writing techniques, patterns for handling complex inputs, structure-aware fuzzing, and protobuf-based fuzzing, see the fuzz-harness-writing technique skill.
The key flag is -fsanitize=fuzzer, which:
Links the libFuzzer runtime (provides main function)
Enables SanitizerCoverage instrumentation for coverage tracking
Disables built-in functions like memcmp
clang++ -fsanitize=fuzzer -g -O2 harness.cc target.cc -o fuzz
Flags explained:
-fsanitize=fuzzer: Enable libFuzzer-g: Add debug symbols (helpful for crash analysis)-O2: Production-level optimizations (recommended for fuzzing)-DNO_MAIN: Define macro if your code has a main functionAddressSanitizer (recommended):
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzz
Multiple sanitizers:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzz
See Also: For detailed sanitizer configuration, common issues, ASAN_OPTIONS flags, and advanced sanitizer usage, see the address-sanitizer and undefined-behavior-sanitizer technique skills.
| Flag | Purpose |
|---|---|
-fsanitize=fuzzer | Enable libFuzzer runtime and instrumentation |
-fsanitize=address | Enable AddressSanitizer (memory error detection) |
-fsanitize=undefined | Enable UndefinedBehaviorSanitizer |
-fsanitize=fuzzer-no-link | Instrument without linking fuzzer (for libraries) |
-g | Include debug symbols |
-O2 |
For projects that produce static libraries:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
make
2. Link the static library with your harness:
clang++ -fsanitize=fuzzer -fsanitize=address harness.cc libmylib.a -o fuzz
project(FuzzTarget)
cmake_minimum_required(VERSION 3.0)
add_executable(fuzz main.cc harness.cc)
target_compile_definitions(fuzz PRIVATE NO_MAIN=1)
target_compile_options(fuzz PRIVATE -g -O2 -fsanitize=fuzzer -fsanitize=address)
target_link_libraries(fuzz -fsanitize=fuzzer -fsanitize=address)
Build with:
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build .
Create a directory for the corpus (can start empty):
mkdir corpus/
Optional but recommended: Provide seed inputs (valid example files):
# For a PNG parser:
cp examples/*.png corpus/
# For a protocol parser:
cp test_packets/*.bin corpus/
Benefits of seed inputs:
The corpus directory contains:
a9993e364706816aba3e25717850c26c9cd0d89d)libFuzzer automatically minimizes corpus entries during fuzzing. To explicitly minimize:
mkdir minimized_corpus/
./fuzz -merge=1 minimized_corpus/ corpus/
This creates a deduplicated, minimized corpus in minimized_corpus/.
See Also: For corpus creation strategies, seed selection, format-specific corpus building, and corpus maintenance, see the fuzzing-corpus technique skill.
./fuzz corpus/
This runs until a crash is found or you stop it (Ctrl+C).
./fuzz -fork=1 -ignore_crashes=1 corpus/
The -fork and -ignore_crashes flags (experimental but widely used) allow fuzzing to continue after finding crashes.
Control input size:
./fuzz -max_len=4000 corpus/
Rule of thumb: 2x the size of minimal realistic input.
Set timeout:
./fuzz -timeout=2 corpus/
Abort test cases that run longer than 2 seconds.
Use a dictionary:
./fuzz -dict=./format.dict corpus/
Close stdout/stderr (speed up fuzzing):
./fuzz -close_fd_mask=3 corpus/
See all options:
./fuzz -help=1
Option 1: Jobs and workers (recommended):
./fuzz -jobs=4 -workers=4 -fork=1 -ignore_crashes=1 corpus/
-jobs=4: Run 4 sequential campaigns-workers=4: Process jobs in parallel with 4 processesOption 2: Fork mode:
./fuzz -fork=4 -ignore_crashes=1 corpus/
Note: For serious multi-core fuzzing, consider switching to AFL++, Honggfuzz, or LibAFL.
Re-run a single crash:
./fuzz ./crash-a9993e364706816aba3e25717850c26c9cd0d89d
Test all inputs in a directory without fuzzing:
./fuzz -runs=0 corpus/
When fuzzing runs, you'll see statistics like:
INFO: Seed: 3517090860
INFO: Loaded 1 modules (9 inline 8-bit counters)
#2 INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 26Mb
#57 NEW cov: 4 ft: 5 corp: 2/4b lim: 4 exec/s: 0 rss: 26Mb
| Output | Meaning |
|---|---|
INITED | Fuzzing initialized |
NEW | New coverage found, added to corpus |
REDUCE | Input minimized while keeping coverage |
cov: N | Number of coverage edges hit |
corp: X/Yb | Corpus size: X entries, Y total bytes |
exec/s: N | Executions per second |
On crash:
==11672== ERROR: libFuzzer: deadly signal
artifact_prefix='./'; Test unit written to ./crash-a9993e364706816aba3e25717850c26c9cd0d89d
0x61,0x62,0x63,
abc
Base64: YWJj
The crash is saved to ./crash-<hash> with the input shown in hex, UTF-8, and Base64.
Reproducibility: Use -seed=<value> to reproduce a fuzzing campaign (single-core only).
Dictionaries help the fuzzer discover interesting inputs faster by providing hints about the input format.
Create a text file with quoted strings (one per line):
# Lines starting with '#' are comments
# Magic bytes
magic="\x89PNG"
magic2="IEND"
# Keywords
"GET"
"POST"
"Content-Type"
# Hex sequences
delimiter="\xFF\xD8\xFF"
./fuzz -dict=./format.dict corpus/
From header files:
grep -o '".*"' header.h > header.dict
From man pages:
man curl | grep -oP '^\s*(--|-)\K\S+' | sed 's/[,.]$//' | sed 's/^/"&/; s/$/&"/' | sort -u > man.dict
From binary strings:
strings ./binary | sed 's/^/"&/; s/$/&"/' > strings.dict
Using LLMs: Ask ChatGPT or similar to generate a dictionary for your format (e.g., "Generate a libFuzzer dictionary for a JSON parser").
See Also: For advanced dictionary generation, format-specific dictionaries, and dictionary optimization strategies, see the fuzzing-dictionaries technique skill.
While libFuzzer shows basic coverage stats (cov: N), detailed coverage analysis requires additional tools.
1. Recompile with coverage instrumentation:
clang++ -fsanitize=fuzzer -fprofile-instr-generate -fcoverage-mapping harness.cc target.cc -o fuzz
2. Run fuzzer to collect coverage:
LLVM_PROFILE_FILE="coverage-%p.profraw" ./fuzz -runs=10000 corpus/
3. Merge coverage data:
llvm-profdata merge -sparse coverage-*.profraw -o coverage.profdata
4. Generate coverage report:
llvm-cov show ./fuzz -instr-profile=coverage.profdata
5. Generate HTML report:
llvm-cov show ./fuzz -instr-profile=coverage.profdata -format=html > coverage.html
Tips:
See Also: For detailed coverage analysis techniques, identifying coverage gaps, systematic coverage improvement, and comparing coverage across fuzzers, see the coverage-analysis technique skill.
ASan detects memory errors like buffer overflows and use-after-free bugs. Highly recommended for fuzzing.
Enable ASan:
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzz
Example ASan output:
==1276163==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000c4ab1
WRITE of size 1 at 0x6020000c4ab1 thread T0
#0 0x55555568631a in check_buf(char*, unsigned long) main.cc:13:25
#1 0x5555556860bf in LLVMFuzzerTestOneInput harness.cc:7:3
Configure ASan with environment variables:
ASAN_OPTIONS=verbosity=1:abort_on_error=1 ./fuzz corpus/
Important flags:
verbosity=1: Show ASan is activedetect_leaks=0: Disable leak detection (leaks reported at end)abort_on_error=1: Call abort() instead of _exit() on errorsDrawbacks:
-rss_limit_mb=0)See Also: For comprehensive ASan configuration, common pitfalls, symbolization, and combining with other sanitizers, see the address-sanitizer technique skill.
UBSan detects undefined behavior like integer overflow, null pointer dereference, etc.
Enable UBSan:
clang++ -fsanitize=fuzzer,undefined -g -O2 harness.cc target.cc -o fuzz
Combine with ASan:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzz
MSan detects uninitialized memory reads. More complex to use (requires rebuilding all dependencies).
clang++ -fsanitize=fuzzer,memory -g -O2 harness.cc target.cc -o fuzz
| Issue | Solution |
|---|---|
| ASan slows fuzzing too much | Use -fsanitize-recover=address for non-fatal errors |
| Out of memory | Set ASAN_OPTIONS=rss_limit_mb=0 or -rss_limit_mb=0 |
| Stack exhaustion | Increase stack size: ASAN_OPTIONS=stack_size=8388608 |
False positives with _FORTIFY_SOURCE | Use -U_FORTIFY_SOURCE flag |
| MSan reports in dependencies | Rebuild all dependencies with |
libpng is a widely-used library for reading/writing PNG images. Bugs can lead to security issues.
1. Get source code:
curl -L -O https://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar xf libpng-1.6.37.tar.xz
cd libpng-1.6.37/
2. Install dependencies:
apt install zlib1g-dev
3. Compile with fuzzing instrumentation:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
make
4. Get a harness (or write your own):
curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc
5. Prepare corpus and dictionary:
mkdir corpus/
curl -o corpus/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png
curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict
6. Link and compile fuzzer:
clang++ -fsanitize=fuzzer -fsanitize=address libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz
7. Run fuzzing campaign:
./fuzz -close_fd_mask=3 -dict=./png.dict corpus/
Harness that finds a division-by-zero bug:
#include <stdint.h>
#include <stddef.h>
double divide(uint32_t numerator, uint32_t denominator) {
// Bug: No check if denominator is zero
return numerator / denominator;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if(size != 2 * sizeof(uint32_t)) {
return 0;
}
uint32_t numerator = *(uint32_t*)(data);
uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));
divide(numerator, denominator);
return 0;
}
Compile and fuzz:
clang++ -fsanitize=fuzzer harness.cc -o fuzz
./fuzz
The fuzzer will quickly find inputs causing a crash.
| Tip | Why It Helps |
|---|---|
| Start with single-core, switch to AFL++ for multi-core | libFuzzer harnesses work with AFL++ |
| Use dictionaries for structured formats | 10-100x faster bug discovery |
Close file descriptors with -close_fd_mask=3 | Speed boost if SUT writes output |
Set reasonable -max_len | Prevents wasted time on huge inputs |
| Run for days/weeks, not minutes | Coverage plateaus take time to break |
| Use seed corpus from test suites | Starts fuzzing from valid inputs |
For highly structured inputs (e.g., complex protocols, file formats), use libprotobuf-mutator:
See structure-aware fuzzing documentation for details.
libFuzzer allows custom mutators for specialized fuzzing:
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed) {
// Custom mutation logic
return new_size;
}
extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize,
unsigned int Seed) {
// Custom crossover logic
return new_size;
}
| Setting | Impact |
|---|---|
-close_fd_mask=3 | Closes stdout/stderr, speeds up fuzzing |
-max_len=<reasonable_size> | Avoids wasting time on huge inputs |
-timeout=<seconds> | Detects hangs, prevents stuck executions |
| Disable ASan for baseline | 2-4x speed boost (but misses memory bugs) |
Use -jobs and -workers | Limited multi-core support |
| Run on Linux | Best platform support and performance |
| Problem | Cause | Solution |
|---|---|---|
| No crashes found after hours | Poor corpus, low coverage | Add seed inputs, use dictionary, check harness |
| Very slow executions/sec (<100) | Target too complex, excessive logging | Optimize target, use -close_fd_mask=3, reduce logging |
| Out of memory | ASan's 20TB virtual memory | Set -rss_limit_mb=0 to disable RSS limit |
| Fuzzer stops after first crash | Default behavior | Use -fork=1 -ignore_crashes=1 to continue |
| Can't reproduce crash | Non-determinism in harness/target | Remove random number generation, global state |
Linking errors with -fsanitize=fuzzer |
| Skill | Use Case |
|---|---|
| fuzz-harness-writing | Detailed guidance on writing effective harnesses, structure-aware fuzzing, and FuzzedDataProvider usage |
| address-sanitizer | Memory error detection configuration, ASAN_OPTIONS, and troubleshooting |
| undefined-behavior-sanitizer | Detecting undefined behavior during fuzzing |
| coverage-analysis | Measuring fuzzing effectiveness and identifying untested code paths |
| fuzzing-corpus | Building and managing seed corpora, corpus minimization strategies |
| fuzzing-dictionaries | Creating format-specific dictionaries for faster bug discovery |
| Skill | When to Consider |
|---|---|
| aflpp | When you need serious multi-core fuzzing, or when libFuzzer coverage plateaus |
| honggfuzz | When you want hardware-based coverage feedback on Linux |
| libafl | When building custom fuzzers or conducting fuzzing research |
Weekly Installs
1.1K
Repository
GitHub Stars
3.9K
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
claude-code945
opencode904
gemini-cli885
codex880
cursor857
github-copilot830
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
| Production optimization level |
-U_FORTIFY_SOURCE | Disable fortification (can interfere with ASan) |
rss: NMb | Resident memory usage |
-fsanitize=memory| Missing libFuzzer runtime |
| Ensure using Clang, check LLVM installation |
| GCC project won't compile with Clang | GCC-specific code | Switch to AFL++ with gcc_plugin instead |
| Coverage not improving | Corpus plateau | Run longer, add dictionary, improve seeds, check coverage report |
| Crashes but ASan doesn't trigger | Memory error not detected without ASan | Recompile with -fsanitize=address |