npx skills add https://github.com/trailofbits/skills --skill fuzzing-obstacles代码库中常包含阻碍有效覆盖率提升的反模糊测试模式。校验和、全局状态(如基于时间的伪随机数生成器)以及验证检查都可能阻止模糊测试器探索更深层的代码路径。本技术展示了如何在模糊测试期间修补被测系统(SUT)以绕过这些障碍,同时保持生产环境行为不变。
许多现实世界的程序在设计时并未考虑模糊测试。它们可能:
这些模式使得模糊测试变得困难,因为:
解决方案是条件编译:在模糊测试构建期间修改代码行为,同时保持生产代码不变。
| 概念 | 描述 |
|---|---|
| SUT 修补 | 修改被测系统使其对模糊测试友好 |
| 条件编译 | 基于编译时标志表现不同的代码 |
| 模糊测试构建模式 | 启用模糊测试特定补丁的特殊构建配置 |
| 误报 | 在模糊测试期间发现的、在生产环境中不可能发生的崩溃 |
| 确定性 | 相同的输入总是产生相同的行为(对模糊测试至关重要) |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
在以下情况应用此技术:
在以下情况跳过此技术:
| 任务 | C/C++ | Rust |
|---|---|---|
| 检查是否为模糊测试构建 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | cfg!(fuzzing) |
| 在模糊测试期间跳过检查 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION return -1; #endif | if !cfg!(fuzzing) { return Err(...) } |
| 常见障碍 | 校验和、伪随机数生成器、基于时间的逻辑 | 校验和、伪随机数生成器、基于时间的逻辑 |
| 支持的模糊测试器 | libFuzzer、AFL++、LibAFL、honggfuzz | cargo-fuzz、libFuzzer |
运行模糊测试器并分析覆盖率,以找到无法到达的代码。常见模式:
rand()、time() 或使用系统种子的 srand() 的调用辅助工具:
-fprofile-instr-generate 进行分析修改障碍,使其在模糊测试构建期间被绕过。
C/C++ 示例:
// 之前:难以逾越的障碍
if (checksum != expected_hash) {
return -1; // 模糊测试器永远无法通过此处
}
// 之后:条件性绕过
if (checksum != expected_hash) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
return -1; // 仅在生产环境中强制执行
#endif
}
// 模糊测试器现在可以探索此检查之后的代码
Rust 示例:
// 之前:难以逾越的障碍
if checksum != expected_hash {
return Err(MyError::Hash); // 模糊测试器永远无法通过此处
}
// 之后:条件性绕过
if checksum != expected_hash {
if !cfg!(fuzzing) {
return Err(MyError::Hash); // 仅在生产环境中强制执行
}
}
// 模糊测试器现在可以探索此检查之后的代码
修补后:
考虑跳过检查是否会引入不可能的程序状态:
如果很可能出现误报,请考虑使用更有针对性的补丁(见下文常见模式)。
使用场景: 哈希/校验和阻碍了所有模糊测试进展
之前:
uint32_t computed = hash_function(data, size);
if (computed != expected_checksum) {
return ERROR_INVALID_HASH;
}
process_data(data, size);
之后:
uint32_t computed = hash_function(data, size);
if (computed != expected_checksum) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
return ERROR_INVALID_HASH;
#endif
}
process_data(data, size);
误报风险: 低 - 如果数据处理不依赖于校验和的正确性
使用场景: 非确定性随机状态阻碍了可重现性
之前:
void initialize() {
srand(time(NULL)); // 每次运行种子不同
}
之后:
void initialize() {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
srand(12345); // 为模糊测试使用固定种子
#else
srand(time(NULL));
#endif
}
误报风险: 低 - 模糊测试器可以使用固定种子探索所有代码路径
使用场景: 必须跳过验证,但下游代码有假设
之前(危险):
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (!validate_config(&config)) {
return -1; // 确保 config.x != 0
}
#endif
int32_t result = 100 / config.x; // 崩溃:模糊测试中除以零!
之后(安全):
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (!validate_config(&config)) {
return -1;
}
#else
// 在模糊测试期间,为失败的验证使用安全默认值
if (!validate_config(&config)) {
config.x = 1; // 防止除以零
config.y = 1;
}
#endif
int32_t result = 100 / config.x; // 在两种构建中都安全
误报风险: 已缓解 - 提供安全默认值而非直接跳过
使用场景: 多步骤验证使得生成有效输入几乎不可能
Rust 示例:
// 之前:多阶段验证
pub fn parse_message(data: &[u8]) -> Result<Message, Error> {
validate_magic_bytes(data)?;
validate_structure(data)?;
validate_checksums(data)?;
validate_crypto_signature(data)?;
deserialize_message(data)
}
// 之后:在模糊测试期间跳过昂贵的验证
pub fn parse_message(data: &[u8]) -> Result<Message, Error> {
validate_magic_bytes(data)?; // 保留廉价检查
if !cfg!(fuzzing) {
validate_structure(data)?;
validate_checksums(data)?;
validate_crypto_signature(data)?;
}
deserialize_message(data)
}
误报风险: 中等 - 反序列化必须能优雅地处理格式错误的数据
| 技巧 | 为何有帮助 |
|---|---|
| 保留廉价验证 | 魔数字节和大小检查能以低成本引导模糊测试器 |
| 为伪随机数生成器使用固定种子 | 在探索所有代码路径时使行为具有确定性 |
| 增量式修补 | 一次跳过一个障碍并测量覆盖率影响 |
| 添加防御性默认值 | 当跳过验证时,提供安全的回退值 |
| 记录所有补丁 | 未来的维护者需要理解模糊测试与生产环境的差异 |
OpenSSL: 使用 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 来修改加密算法行为。例如,在 crypto/cmp/cmp_vfy.c 中,某些签名检查在模糊测试期间被放宽,以允许更深入地探索证书验证逻辑。
ogg crate (Rust): 使用 cfg!(fuzzing) 在模糊测试期间跳过校验和验证。这使得模糊测试器能够探索音频处理代码,而无需花费精力猜测正确的校验和。
应用补丁后,量化改进:
llvm-cov 或 cargo-cov 查看新的可到达行有效的补丁通常能将覆盖率提高 10-50% 或更多。
障碍修补与以下技术结合良好:
| 反面模式 | 问题 | 正确方法 |
|---|---|---|
| 跳过所有验证 | 产生误报和不稳定的模糊测试 | 仅跳过阻碍覆盖率的特定障碍 |
| 不进行风险评估 | 误报浪费时间并掩盖真正的错误 | 分析下游代码的假设 |
| 忘记记录补丁 | 未来的维护者不理解差异 | 添加注释解释补丁为何安全 |
| 修补而不测量 | 不知道是否有帮助 | 比较修补前后的覆盖率 |
| 过度修补 | 使模糊测试构建与生产环境差异过大 | 最小化构建间的差异 |
libFuzzer 在编译期间自动定义 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION。
# C++ 编译
clang++ -g -fsanitize=fuzzer,address -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION \
harness.cc target.cc -o fuzzer
# 该宏通常由 -fsanitize=fuzzer 自动定义
clang++ -g -fsanitize=fuzzer,address harness.cc target.cc -o fuzzer
集成技巧:
#ifdef 检查宏AFL++ 在使用其编译器包装器时也会定义 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION。
# 使用 AFL++ 包装器编译
afl-clang-fast++ -g -fsanitize=address target.cc harness.cc -o fuzzer
# 宏由 afl-clang-fast 自动定义
集成技巧:
afl-clang-fast 或 afl-clang-lto 实现自动宏定义AFL_LLVM_LAF_ALL 进行额外的输入到状态转换honggfuzz 在构建目标时也支持该宏。
# 编译
hfuzz-clang++ -g -fsanitize=address target.cc harness.cc -o fuzzer
集成技巧:
hfuzz-clang 或 hfuzz-clang++ 包装器cargo-fuzz 在构建期间自动设置 fuzzing cfg 选项。
# 构建模糊测试目标(cfg!(fuzzing) 自动设置)
cargo fuzz build fuzz_target_name
# 运行模糊测试目标
cargo fuzz run fuzz_target_name
集成技巧:
cfg!(fuzzing) 进行运行时检查#[cfg(fuzzing)] 进行编译时条件编译cargo fuzz 构建期间设置,而不是常规的 cargo buildRUSTFLAGS="--cfg fuzzing" 手动启用以进行测试LibAFL 支持用于 C/C++ 编写的目标的 C/C++ 宏。
# 编译
clang++ -g -fsanitize=address -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION \
target.cc -c -o target.o
集成技巧:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 修补后覆盖率未提升 | 识别了错误的障碍 | 分析执行情况以找到实际瓶颈 |
| 许多误报崩溃 | 下游代码有假设 | 添加防御性默认值或部分验证 |
| 代码编译方式不同 | 宏未在所有构建配置中定义 | 在所有源文件和依赖项中验证宏 |
| 模糊测试器在修补代码中发现错误 | 补丁引入了无效状态 | 审查补丁的状态不变量;考虑更安全的方法 |
| 无法复现生产环境错误 | 构建差异过大 | 最小化补丁;保留对状态关键检查的验证 |
| 技能 | 如何应用 |
|---|---|
| libfuzzer | 自动定义 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
| aflpp | 通过编译器包装器支持该宏 |
| honggfuzz | 使用该宏进行条件编译 |
| cargo-fuzz | 为 Rust 条件编译设置 cfg!(fuzzing) |
| 技能 | 关系 |
|---|---|
| fuzz-harness-writing | 更好的测试工具可能避免障碍;修补支持更深层探索 |
| coverage-analysis | 使用覆盖率识别障碍并衡量补丁效果 |
| corpus-seeding | 种子语料库可以帮助克服障碍而无需修补 |
| dictionary-generation | 字典有助于处理魔数字节,但对校验和或复杂验证无效 |
OpenSSL 模糊测试文档 OpenSSL 的模糊测试基础设施展示了 FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 的大规模使用。该项目使用此宏来修改加密验证、证书解析和其他安全关键代码路径,以便在保持生产环境正确性的同时实现更深入的模糊测试。
LibFuzzer 标志文档 libFuzzer 的官方 LLVM 文档,包括模糊测试器如何定义编译器宏以及如何有效使用它们。涵盖与消毒剂和覆盖率插桩的集成。
Rust cfg 属性参考 Rust 条件编译的完整参考,包括 cfg!(fuzzing) 和 cfg!(test)。解释了编译时与运行时条件编译以及最佳实践。
每周安装量
1.2K
代码仓库
GitHub 星标数
3.9K
首次出现
Jan 19, 2026
安全审计
安装于
claude-code1.0K
codex974
opencode920
gemini-cli903
cursor873
github-copilot845
Codebases often contain anti-fuzzing patterns that prevent effective coverage. Checksums, global state (like time-seeded PRNGs), and validation checks can block the fuzzer from exploring deeper code paths. This technique shows how to patch your System Under Test (SUT) to bypass these obstacles during fuzzing while preserving production behavior.
Many real-world programs were not designed with fuzzing in mind. They may:
These patterns make fuzzing difficult because:
The solution is conditional compilation: modify code behavior during fuzzing builds while keeping production code unchanged.
| Concept | Description |
|---|---|
| SUT Patching | Modifying System Under Test to be fuzzing-friendly |
| Conditional Compilation | Code that behaves differently based on compile-time flags |
| Fuzzing Build Mode | Special build configuration that enables fuzzing-specific patches |
| False Positives | Crashes found during fuzzing that cannot occur in production |
| Determinism | Same input always produces same behavior (critical for fuzzing) |
Apply this technique when:
Skip this technique when:
| Task | C/C++ | Rust |
|---|---|---|
| Check if fuzzing build | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | cfg!(fuzzing) |
| Skip check during fuzzing | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION return -1; #endif | if !cfg!(fuzzing) { return Err(...) } |
| Common obstacles | Checksums, PRNGs, time-based logic | Checksums, PRNGs, time-based logic |
| Supported fuzzers | libFuzzer, AFL++, LibAFL, honggfuzz | cargo-fuzz, libFuzzer |
Run the fuzzer and analyze coverage to find code that's unreachable. Common patterns:
rand(), time(), or srand() with system seedsTools to help:
-fprofile-instr-generateModify the obstacle to bypass it during fuzzing builds.
C/C++ Example:
// Before: Hard obstacle
if (checksum != expected_hash) {
return -1; // Fuzzer never gets past here
}
// After: Conditional bypass
if (checksum != expected_hash) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
return -1; // Only enforced in production
#endif
}
// Fuzzer can now explore code beyond this check
Rust Example:
// Before: Hard obstacle
if checksum != expected_hash {
return Err(MyError::Hash); // Fuzzer never gets past here
}
// After: Conditional bypass
if checksum != expected_hash {
if !cfg!(fuzzing) {
return Err(MyError::Hash); // Only enforced in production
}
}
// Fuzzer can now explore code beyond this check
After patching:
Consider whether skipping the check introduces impossible program states:
If false positives are likely, consider a more targeted patch (see Common Patterns below).
Use Case: Hash/checksum blocks all fuzzer progress
Before:
uint32_t computed = hash_function(data, size);
if (computed != expected_checksum) {
return ERROR_INVALID_HASH;
}
process_data(data, size);
After:
uint32_t computed = hash_function(data, size);
if (computed != expected_checksum) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
return ERROR_INVALID_HASH;
#endif
}
process_data(data, size);
False positive risk: LOW - If data processing doesn't depend on checksum correctness
Use Case: Non-deterministic random state prevents reproducibility
Before:
void initialize() {
srand(time(NULL)); // Different seed each run
}
After:
void initialize() {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
srand(12345); // Fixed seed for fuzzing
#else
srand(time(NULL));
#endif
}
False positive risk: LOW - Fuzzer can explore all code paths with fixed seed
Use Case: Validation must be skipped but downstream code has assumptions
Before (Dangerous):
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (!validate_config(&config)) {
return -1; // Ensures config.x != 0
}
#endif
int32_t result = 100 / config.x; // CRASH: Division by zero in fuzzing!
After (Safe):
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (!validate_config(&config)) {
return -1;
}
#else
// During fuzzing, use safe defaults for failed validation
if (!validate_config(&config)) {
config.x = 1; // Prevent division by zero
config.y = 1;
}
#endif
int32_t result = 100 / config.x; // Safe in both builds
False positive risk: MITIGATED - Provides safe defaults instead of skipping
Use Case: Multi-step validation makes valid input generation nearly impossible
Rust Example:
// Before: Multiple validation stages
pub fn parse_message(data: &[u8]) -> Result<Message, Error> {
validate_magic_bytes(data)?;
validate_structure(data)?;
validate_checksums(data)?;
validate_crypto_signature(data)?;
deserialize_message(data)
}
// After: Skip expensive validation during fuzzing
pub fn parse_message(data: &[u8]) -> Result<Message, Error> {
validate_magic_bytes(data)?; // Keep cheap checks
if !cfg!(fuzzing) {
validate_structure(data)?;
validate_checksums(data)?;
validate_crypto_signature(data)?;
}
deserialize_message(data)
}
False positive risk: MEDIUM - Deserialization must handle malformed data gracefully
| Tip | Why It Helps |
|---|---|
| Keep cheap validation | Magic bytes and size checks guide fuzzer without much cost |
| Use fixed seeds for PRNGs | Makes behavior deterministic while exploring all code paths |
| Patch incrementally | Skip one obstacle at a time and measure coverage impact |
| Add defensive defaults | When skipping validation, provide safe fallback values |
| Document all patches | Future maintainers need to understand fuzzing vs. production differences |
OpenSSL: Uses FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION to modify cryptographic algorithm behavior. For example, in crypto/cmp/cmp_vfy.c, certain signature checks are relaxed during fuzzing to allow deeper exploration of certificate validation logic.
ogg crate (Rust): Uses cfg!(fuzzing) to skip checksum verification during fuzzing. This allows the fuzzer to explore audio processing code without spending effort guessing correct checksums.
After applying patches, quantify the improvement:
llvm-cov or cargo-cov to see new reachable linesEffective patches typically increase coverage by 10-50% or more.
Obstacle patching works well with:
| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
| Skip all validation wholesale | Creates false positives and unstable fuzzing | Skip only specific obstacles that block coverage |
| No risk assessment | False positives waste time and hide real bugs | Analyze downstream code for assumptions |
| Forget to document patches | Future maintainers don't understand the differences | Add comments explaining why patch is safe |
| Patch without measuring | Don't know if it helped | Compare coverage before and after |
| Over-patching | Makes fuzzing build diverge too much from production | Minimize differences between builds |
libFuzzer automatically defines FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION during compilation.
# C++ compilation
clang++ -g -fsanitize=fuzzer,address -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION \
harness.cc target.cc -o fuzzer
# The macro is usually defined automatically by -fsanitize=fuzzer
clang++ -g -fsanitize=fuzzer,address harness.cc target.cc -o fuzzer
Integration tips:
#ifdef to check for the macroAFL++ also defines FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION when using its compiler wrappers.
# Compilation with AFL++ wrappers
afl-clang-fast++ -g -fsanitize=address target.cc harness.cc -o fuzzer
# The macro is defined automatically by afl-clang-fast
Integration tips:
afl-clang-fast or afl-clang-lto for automatic macro definitionAFL_LLVM_LAF_ALL for additional input-to-state transformationshonggfuzz also supports the macro when building targets.
# Compilation
hfuzz-clang++ -g -fsanitize=address target.cc harness.cc -o fuzzer
Integration tips:
hfuzz-clang or hfuzz-clang++ wrapperscargo-fuzz automatically sets the fuzzing cfg option during builds.
# Build fuzz target (cfg!(fuzzing) is automatically set)
cargo fuzz build fuzz_target_name
# Run fuzz target
cargo fuzz run fuzz_target_name
Integration tips:
cfg!(fuzzing) for runtime checks in production builds#[cfg(fuzzing)] for compile-time conditional compilationcargo fuzz builds, not regular cargo buildRUSTFLAGS="--cfg fuzzing" for testingLibAFL supports the C/C++ macro for targets written in C/C++.
# Compilation
clang++ -g -fsanitize=address -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION \
target.cc -c -o target.o
Integration tips:
| Issue | Cause | Solution |
|---|---|---|
| Coverage doesn't improve after patching | Wrong obstacle identified | Profile execution to find actual bottleneck |
| Many false positive crashes | Downstream code has assumptions | Add defensive defaults or partial validation |
| Code compiles differently | Macro not defined in all build configs | Verify macro in all source files and dependencies |
| Fuzzer finds bugs in patched code | Patch introduced invalid states | Review patch for state invariants; consider safer approach |
| Can't reproduce production bugs | Build differences too large | Minimize patches; keep validation for state-critical checks |
| Skill | How It Applies |
|---|---|
| libfuzzer | Defines FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION automatically |
| aflpp | Supports the macro via compiler wrappers |
| honggfuzz | Uses the macro for conditional compilation |
| cargo-fuzz | Sets cfg!(fuzzing) for Rust conditional compilation |
| Skill | Relationship |
|---|---|
| fuzz-harness-writing | Better harnesses may avoid obstacles; patching enables deeper exploration |
| coverage-analysis | Use coverage to identify obstacles and measure patch effectiveness |
| corpus-seeding | Seed corpus can help overcome obstacles without patching |
| dictionary-generation | Dictionaries help with magic bytes but not checksums or complex validation |
OpenSSL Fuzzing Documentation OpenSSL's fuzzing infrastructure demonstrates large-scale use of FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION. The project uses this macro to modify cryptographic validation, certificate parsing, and other security-critical code paths to enable deeper fuzzing while maintaining production correctness.
LibFuzzer Documentation on Flags Official LLVM documentation for libFuzzer, including how the fuzzer defines compiler macros and how to use them effectively. Covers integration with sanitizers and coverage instrumentation.
Rust cfg Attribute Reference Complete reference for Rust conditional compilation, including cfg!(fuzzing) and cfg!(test). Explains compile-time vs. runtime conditional compilation and best practices.
Weekly Installs
1.2K
Repository
GitHub Stars
3.9K
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code1.0K
codex974
opencode920
gemini-cli903
cursor873
github-copilot845
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装