
libFuzzer 要求你提供一个 FuzzTarget 函数
libFuzzer 不是黑盒扫描器,它需要你把待测逻辑封装成一个接受 const uint8_t* 和 size_t 的 C++ 函数。这个函数就是模糊测试的入口,libFuzzer 会不断传入随机字节流来触发边界行为。
常见错误是直接把原始输入(比如 JSON 字符串)硬编码进函数,或者忘了处理空指针、零长度等边界情况——这会导致崩溃被误报为漏洞,或漏掉真实问题。
- 函数签名必须是
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - 不要在函数内调用
exit()、abort()或抛未捕获异常(除非你明确想让 libFuzzer 捕获该崩溃) - 如果被测代码内部有内存分配/释放逻辑,确保
Data内容不会越界读取(例如用Size做长度校验后再构造std::string_view或std::vector) - 避免依赖全局状态或文件系统;每次调用应尽可能独立
编译时必须启用 -fsanitize=address,fuzzer 且禁用优化
libFuzzer 本身不检测内存错误,它依赖 ASan(AddressSanitizer)等 sanitizer 来发现堆溢出、UAF、栈溢出等问题。没有 sanitizer,多数 C++ 安全漏洞根本不会暴露为崩溃。
典型错误是只加了 -fsanitize=fuzzer 却漏掉 address 或 undefined,结果跑半天只看到 “timeout” 或 “done”,却没触发任何 crash。
立即学习“C++免费学习笔记(深入)”;
- 推荐完整编译命令:
clang++ -g -O1 -fsanitize=address,fuzzer -fno-omit-frame-pointer fuzz_target.cpp -o fuzz_target -
-O1是安全阈值;-O2及以上可能让 sanitizer 失效或掩盖崩溃路径 - 链接阶段不能漏掉
-fsanitize=fuzzer,否则会报错undefined reference to __fuzzing_entry_point - 若被测代码含 C++ 异常,建议加
-fsanitize=undefined捕获未定义行为(如 signed integer overflow)
崩溃复现必须用原始 crash-xxx 文件,不能靠日志猜
libFuzzer 发现崩溃后会生成类似 crash-123abc... 的二进制文件。这些文件内容就是触发崩溃的最小输入字节序列。靠日志里 “heap-use-after-free on address 0x…” 这类信息去手动构造输入,99% 会失败。
原因在于:崩溃路径高度依赖输入中特定字节的位置、长度和数值组合,微小改动就绕过触发点。
- 复现命令:
./fuzz_target ./crash-123abc... - 调试命令:
gdb --args ./fuzz_target ./crash-123abc...,然后r即可精准停在崩溃点 - 如果
crash-xxx文件在另一台机器上无法复现,大概率是编译环境不一致(ASan 版本、libc++ vs libstdc++、甚至 clang 版本差异) - 别删
corpus目录——它存着 libFuzzer 积累的有效输入,重启 fuzz 时加-runs=0可快速验证已有用例是否仍触发崩溃
自定义字典和 -max_len 对发现 C++ 解析类漏洞很关键
纯随机字节对解析器(JSON/XML/自定义协议)效率极低。libFuzzer 支持通过 -dict 提供关键词字典,大幅提升覆盖关键字、结构标记、转义序列的概率。
而 -max_len 控制输入最大长度,设得太小(如默认 4096)可能错过长字段导致的堆膨胀或整数截断漏洞;设得太大(如 1MB)又会让单次执行过慢,拖垮整体 fuzz 效率。
- 字典示例(
json.dict):"{" "}" "[" "]" ":" "," "null" "true" "false" "\"" - 启动命令:
./fuzz_target -dict=json.dict -max_len=65536 corpus_dir/ - 对 C++ string/stringstream 解析逻辑,建议从
-max_len=1024开始逐步加大,观察执行时间是否稳定在 100ms 内 - 若目标函数内部有
memcpy(dst, src, n)且n来自输入,务必检查n是否受Size约束——这是整数溢出+越界写的高发区
LLVMFuzzerTestOneInput,而是让被测逻辑足够“干净”:没有全局锁、没有随机数依赖、没有外部网络调用。很多 C++ 项目卡在这一步,不是工具不会用,是代码本身没为可测试性设计。











