libFuzzer 必须静态链接到目标二进制,使用 clang++ 编译并定义 extern "C" int LLVMFuzzerTestOneInput(const uint8_t*, size_t);禁用异常与非 abort() 终止;需提供初始语料以提升覆盖率收敛。

libFuzzer 需要链接到目标二进制,不能单独运行
libFuzzer 不是独立工具,它本质是一个静态链接的运行时库,必须和你的 C++ 代码一起编译、链接成一个可执行文件。直接调用 libfuzzer 命令会失败——它根本不是系统命令,也没有预装的可执行入口。
- 常见错误现象:
command not found: libfuzzer或运行时报LLVMFuzzerTestOneInput not defined - 正确做法:用
clang++(推荐)或clang编译,带上-fsanitize=fuzzer,并确保定义了LLVMFuzzerTestOneInput函数 - 不支持 GCC:libFuzzer 是 LLVM/Clang 生态组件,
-fsanitize=fuzzer在 GCC 下无效 - 调试建议:先关掉其他 sanitizer(如
-fsanitize=address),确认 fuzz 入口能跑通;再叠加启用 ASan/UBSan 提升 crash 可读性
LLVMFuzzerTestOneInput 必须是 extern "C" C 链接函数
C++ 名字修饰(name mangling)会让链接器找不到入口点。libFuzzer 要求该函数以 C ABI 暴露,否则链接失败或运行时崩溃。
- 常见错误现象:链接时报
undefined reference to 'LLVMFuzzerTestOneInput',即使函数看起来写对了 - 必须写成:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // 你的测试逻辑 return 0; } - 参数不能改名或换类型:
const uint8_t*和size_t是硬性约定,用char*或std::vector<uint8_t>会触发未定义行为 - 返回值必须是
int:非零表示异常退出(比如发现 bug),但通常只返回 0;libFuzzer 不依赖返回值做路径裁剪,只是兼容 POSIX 习惯
fuzz target 里别 throw 异常,也别用 std::abort() 以外的终止方式
libFuzzer 运行时会捕获信号(如 SIGSEGV/SIGABRT),但 C++ 异常和某些终止方式会绕过它的 crash 捕获机制,导致 bug 漏报或进程静默退出。
- 常见错误现象:代码明明访问了野指针,fuzzer 却没报告 crash,反而继续跑;或者抛出
std::runtime_error后整个进程被std::terminate()杀掉,没留下 stack trace - 建议统一用
abort()或触发 ASan 报告(如解引用空指针、越界读)来暴露问题 - 如果必须用异常(比如已有库强制抛出),需在
LLVMFuzzerTestOneInput外层加try/catch(...)并转为abort(),否则 libFuzzer 看不到 - 性能影响:异常栈展开开销大,且干扰覆盖率反馈;libFuzzer 的核心优势在于轻量级插桩 + 快速反馈,异常处理会拖慢 fuzz 循环
初始语料(corpus)不是可选的,而是覆盖率收敛的关键
从空输入开始 fuzz,libFuzzer 很难自己“猜出”合法的输入结构(比如 JSON 格式、协议头、图像 magic bytes)。没有初始语料,可能卡在 trivial input 上几小时都进不了深层分支。
立即学习“C++免费学习笔记(深入)”;
- 使用场景:哪怕只有 1–2 个最简合法输入(如
"{}"对应 JSON 解析器),也能让覆盖率快速突破第一层 if 判断 - 创建方法:手动建目录,放几个小文件,每行或每个文件是一个 test case;启动时用
./fuzzer ./corpus_dir - 注意路径权限:libFuzzer 默认会对 corpus 目录写入新发现的 case,确保你有写权限,否则会静默跳过保存
- 容易踩的坑:把 corpus 放在 NFS 或 Docker volume 边界上,I/O 延迟高会导致 fuzz 速度暴跌;本地 ext4/tmpfs 最稳
LLVMFuzzerTestOneInput 的那块内存,到底在模拟什么?协议字段边界、状态机阶段、还是嵌套层级?漏掉这个前提,再好的配置也只是在随机比特里碰运气。



















