
libFuzzer 需要和 AddressSanitizer 一起链接吗?
必须一起链接。libFuzzer 本身不检测内存错误,它只负责生成输入、驱动目标函数、发现崩溃;而崩溃是否由越界访问、UAF 或栈溢出引发,得靠 ASan(AddressSanitizer)这类 sanitizer 实时拦截并报告。单独用 libFuzzer,很多真实 bug 会静默跳过或表现为不可复现的随机 crash。
典型链接方式是同时启用 -fsanitize=address,fuzzer(Clang),这会让编译器自动链接 libclang_rt.fuzzer 和 libclang_rt.asan。漏掉 address(或 undefined、memory 等)会导致 sanitizer 失效,但 fuzzing 还能跑——只是你根本不知道 bug 是什么类型。
-
-fsanitize=address,fuzzer是最常用组合;加undefined可捕获整数溢出、未定义行为,但会显著拖慢速度 - 不能只写
-fsanitize=fuzzer:Clang 会报错 “fuzzer requires other sanitizer” - 确保所有 .o 文件(包括依赖的静态库)都用相同 sanitizer 标志编译,否则链接时可能符号冲突或运行时 ASan 不生效
如何编写一个符合 libFuzzer 要求的 fuzz target 函数?
入口函数签名必须严格为 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)。这不是约定,是 libFuzzer 的硬性 ABI 要求——它通过 dlsym 查找这个符号,拼错大小写、缺 extern "C"、参数类型不符都会导致 “no fuzz target found” 错误。
关键点不是“怎么写逻辑”,而是“怎么避免干扰 fuzzing 进程”:
立即学习“C++免费学习笔记(深入)”;
- 禁止调用
exit()、abort()、std::terminate()—— 应让异常传播到顶层,由 libFuzzer 捕获并记录 - 避免全局状态累积(如 static vector push_back 不清空),否则后续输入可能因残留数据 crash,造成误报
- 如果被测函数抛异常,确保没被中间 catch 吞掉;libFuzzer 默认能捕获未处理异常
- 示例中常见错误:把
std::string(Data, Size)直接传给解析函数——若 Data 含 null 字节,字符串提前截断,实际喂给 parser 的不是完整输入
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 2) return 0; // 快速拒绝太短输入
try {
// 正确:用原始指针+长度构造 std::string_view 或直接传入解析器
auto input = std::string_view(reinterpret_cast(Data), Size);
my_parser(input); // 假设这是你要 fuzz 的函数
} catch (...) {
return 0; // 让 libFuzzer 记录这次异常
}
return 0;
}
为什么用 -fsanitize=fuzzer 时出现 “undefined symbol: __sanitizer_acquire_crash_state”?
这是典型的 sanitizer 运行时库链接缺失或版本不匹配。libFuzzer 依赖 Clang 自带的 sanitizer 运行时(libclang_rt.fuzzer 和配套的 asan/ubsan 库),但如果你混用了系统 GCC 工具链、旧版 Clang,或手动指定 -L 路径覆盖了默认路径,就容易触发该错误。
排查顺序如下:
- 确认全程使用同一套 Clang:检查
clang++ --version和clang++ -print-libgcc-file-name输出路径是否一致 - 不要手动加
-lclang_rt.fuzzer:Clang 在识别-fsanitize=fuzzer时会自动插入正确路径和库,手动加反而易出错 - 检查是否误启用了
-static-libsan:它会把 sanitizer 打包进可执行文件,但 libFuzzer 的初始化代码可能找不到动态符号,建议先禁用 -
macOS 上还需额外加
-fsanitize=fuzzer,address -stdlib=libc++,因为系统默认 libstdc++ 不兼容 ASan
如何让 fuzz target 支持自定义字典和语料库?
libFuzzer 通过命令行参数控制语料行为,不需要改代码。字典(dictionary)用于引导变异朝特定 token(如 magic bytes、协议关键字)方向搜索;语料(corpus)是已知有效的输入集合,用来 seed 初始测试集。
核心参数组合:
-
-dict=parser.dict:字典文件每行一个 token,例如"HTTP/"、"\x00\x01\x02"(十六进制需用 \x 表示) -
./fuzzer corpus_dir/:运行时读取corpus_dir/下所有文件作为初始语料;若目录不存在,libFuzzer 会创建它并保存新发现的 crash/minimized inputs -
-max_len=4096:限制单次输入最大长度,防止 OOM;默认 4096,对解析器类 target 常需调小(如 512)提升效率 -
-timeout=20:单次执行超时秒数,避免死循环拖垮 fuzzing 进程
注意:字典内容必须是 ASCII 可见字符或合法转义序列;二进制 token(如 JPEG SOI \xFF\xD8)必须写成 "\xff\xd8",不能用 raw bytes 写入文件,否则 libFuzzer 解析失败。











