name: address-sanitizer type: technique description: > 地址净化器在模糊测试期间检测内存错误。 当模糊测试C/C++代码时使用,以发现缓冲区溢出和使用后释放错误。
地址净化器 (ASan)
地址净化器 (ASan) 是一种广泛采用的内存错误检测工具,广泛用于软件测试,特别是模糊测试。它有助于检测内存损坏错误,这些错误可能被忽视,如缓冲区溢出、使用后释放错误和其他内存安全违规。
概述
ASan是模糊测试中的标准实践,因为其在识别内存漏洞方面的有效性。它在编译时对代码进行插装,以跟踪内存分配和访问,在运行时检测非法操作。
关键概念
| 概念 | 描述 |
|---|---|
| 插装 | ASan在编译期间为内存操作添加运行时检查 |
| 影子内存 | 映射20TB虚拟内存以跟踪分配状态 |
| 性能成本 | 与非插装代码相比,大约有2-4倍的减速 |
| 检测范围 | 发现缓冲区溢出、使用后释放、双重释放和内存泄漏 |
何时应用
应用此技术当:
- 模糊测试C/C++代码以查找内存安全漏洞
- 测试带有不安全块的Rust代码
- 调试与内存损坏相关的崩溃
- 运行怀疑有内存错误的单元测试
跳过此技术当:
- 运行生产代码(ASan可能降低安全性)
- 平台是Windows或macOS(ASan支持有限)
- 性能开销对您的用例不可接受
- 模糊测试纯安全语言无FFI(例如,纯Go、纯Java)
快速参考
| 任务 | 命令/模式 |
|---|---|
| 启用ASan (Clang/GCC) | -fsanitize=address |
| 启用详细输出 | ASAN_OPTIONS=verbosity=1 |
| 禁用泄漏检测 | ASAN_OPTIONS=detect_leaks=0 |
| 错误时强制中止 | ASAN_OPTIONS=abort_on_error=1 |
| 多个选项 | ASAN_OPTIONS=verbosity=1:abort_on_error=1 |
分步指南
步骤1:用ASan编译
用 -fsanitize=address 标志编译和链接您的代码:
clang -fsanitize=address -g -o my_program my_program.c
推荐使用 -g 标志以在ASan检测错误时获得更好的堆栈跟踪。
步骤2:配置ASan选项
设置 ASAN_OPTIONS 环境变量以配置ASan行为:
export ASAN_OPTIONS=verbosity=1:abort_on_error=1:detect_leaks=0
步骤3:运行您的程序
执行ASan插装的二进制文件。当检测到内存错误时,ASan将打印详细报告:
./my_program
步骤4:调整模糊测试器内存限制
ASan需要大约20TB虚拟内存。禁用模糊测试器内存限制:
- libFuzzer:
-rss_limit_mb=0 - AFL++:
-m none
常见模式
模式:基本ASan集成
用例: 带有ASan的标准模糊测试设置
之前:
clang -o fuzz_target fuzz_target.c
./fuzz_target
之后:
clang -fsanitize=address -g -o fuzz_target fuzz_target.c
ASAN_OPTIONS=verbosity=1:abort_on_error=1 ./fuzz_target
模式:ASan与单元测试
用例: 为单元测试套件启用ASan
之前:
gcc -o test_suite test_suite.c -lcheck
./test_suite
之后:
gcc -fsanitize=address -g -o test_suite test_suite.c -lcheck
ASAN_OPTIONS=detect_leaks=1 ./test_suite
高级用法
技巧与窍门
| 技巧 | 为什么有帮助 |
|---|---|
使用 -g 标志 |
提供详细的堆栈跟踪用于调试 |
设置 verbosity=1 |
在程序启动前确认ASan已启用 |
| 在模糊测试期间禁用泄漏检测 | 泄漏检测不会导致立即崩溃,会干扰输出 |
启用 abort_on_error=1 |
一些模糊测试器需要 abort() 而不是 _exit() |
理解ASan报告
当ASan检测到内存错误时,它会打印详细报告,包括:
- 错误类型:缓冲区溢出、使用后释放等。
- 堆栈跟踪:错误发生的位置
- 分配/释放跟踪:内存分配/释放的位置
- 内存映射:错误周围的影子内存状态
示例ASan报告:
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300000eff4 at pc 0x00000048e6a3
READ of size 4 at 0x60300000eff4 thread T0
#0 0x48e6a2 in main /path/to/file.c:42
结合其他净化器
ASan可以与其他净化器结合以进行全面检测:
clang -fsanitize=address,undefined -g -o fuzz_target fuzz_target.c
平台特定注意事项
Linux:完整的ASan支持,性能最佳 macOS:支持有限,一些功能可能不工作 Windows:实验性支持,不建议用于生产模糊测试
反模式
| 反模式 | 问题 | 正确方法 |
|---|---|---|
| 在生产中使用ASan | 可能使应用程序更不安全 | 仅将ASan用于测试 |
| 不禁用内存限制 | 模糊测试器可能因20TB虚拟内存而杀死进程 | 设置 -rss_limit_mb=0 或 -m none |
| 忽略泄漏报告 | 内存泄漏表示资源管理问题 | 在模糊测试活动结束时审查泄漏报告 |
工具特定指导
libFuzzer
用模糊测试器和地址净化器编译:
clang++ -fsanitize=fuzzer,address -g harness.cc -o fuzz
运行无限RSS:
./fuzz -rss_limit_mb=0
集成提示:
- 始终将
-fsanitize=fuzzer与-fsanitize=address结合使用 - 使用
-g以在崩溃报告中获得详细堆栈跟踪 - 考虑
ASAN_OPTIONS=abort_on_error=1以更好地处理崩溃
参见:libFuzzer: AddressSanitizer
AFL++
使用 AFL_USE_ASAN 环境变量:
AFL_USE_ASAN=1 afl-clang-fast++ -g harness.cc -o fuzz
运行无限内存:
afl-fuzz -m none -i input_dir -o output_dir ./fuzz
集成提示:
AFL_USE_ASAN=1自动添加正确的编译标志- 使用
-m none禁用AFL++的内存限制 - 考虑
AFL_MAP_SIZE用于具有大覆盖映射的程序
cargo-fuzz (Rust)
使用 --sanitizer=address 标志:
cargo fuzz run fuzz_target --sanitizer=address
或在 fuzz/Cargo.toml 中配置:
[profile.release]
opt-level = 3
debug = true
集成提示:
- ASan对于模糊测试不安全的Rust代码或FFI边界很有用
- 安全的Rust代码可能受益较少(编译器已防止许多错误)
- 专注于不安全块、原始指针和C库绑定
参见:cargo-fuzz: AddressSanitizer
honggfuzz
用ASan编译并链接honggfuzz:
honggfuzz -i input_dir -o output_dir -- ./fuzz_target_asan
编译目标:
hfuzz-clang -fsanitize=address -g target.c -o fuzz_target_asan
集成提示:
- honggfuzz与ASan开箱即用工作良好
- 使用反馈驱动模式以更好地覆盖与净化器
- 监控内存使用,因为ASan增加内存占用
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 模糊测试器立即杀死进程 | 内存限制对ASan的20TB虚拟内存太低 | 使用 -rss_limit_mb=0 (libFuzzer) 或 -m none (AFL++) |
| “ASan运行时未初始化” | 链接顺序错误或缺少运行时 | 确保 -fsanitize=address 用于编译和链接 |
| 泄漏报告干扰输出 | LeakSanitizer默认启用 | 设置 ASAN_OPTIONS=detect_leaks=0 |
| 性能差 (>4x 减速) | 调试模式或未优化构建 | 编译时使用 -O2 或 -O3 连同 -fsanitize=address |
| ASan未检测到明显错误 | 二进制未插装 | 用 ASAN_OPTIONS=verbosity=1 检查ASan是否打印启动信息 |
| 误报 | 拦截器冲突 | 检查ASan FAQ了解特定库的已知问题 |
相关技能
使用此技术的工具
| 技能 | 如何应用 |
|---|---|
| libfuzzer | 用 -fsanitize=fuzzer,address 编译以集成模糊测试与内存错误检测 |
| aflpp | 在编译期间使用 AFL_USE_ASAN=1 环境变量 |
| cargo-fuzz | 使用 --sanitizer=address 标志为Rust模糊测试目标启用ASan |
| honggfuzz | 用 -fsanitize=address 编译目标以进行ASan插装的模糊测试 |
相关技术
| 技能 | 关系 |
|---|---|
| undefined-behavior-sanitizer | 常与ASan结合使用以进行全面错误检测(未定义行为 + 内存错误) |
| fuzz-harness-writing | 必须设计以处理ASan检测的崩溃并避免误报 |
| coverage-analysis | 覆盖引导模糊测试帮助触发ASan可以检测内存错误的代码路径 |
资源
关键外部资源
AddressSanitizer on Google Sanitizers Wiki
官方ASan文档涵盖:
- 算法和实现细节
- 检测到的错误类型的完整列表
- 性能特征和开销
- 平台特定行为
- 已知限制和不兼容性
所有净化器共享的通用配置标志:
verbosity:控制诊断输出级别log_path:将净化器输出重定向到文件symbolize:在报告中启用/禁用符号解析external_symbolizer_path:使用自定义符号器
ASan特定配置选项:
detect_leaks:控制内存泄漏检测abort_on_error:在错误时调用abort()对比_exit()detect_stack_use_after_return:检测堆栈使用后返回错误check_initialization_order:查找初始化顺序错误
常见陷阱和解决方案:
- 链接顺序问题
- 与其他工具的冲突
- 平台特定问题
- 性能调优提示
Clang AddressSanitizer Documentation
Clang特定指导:
- 编译标志和选项
- 与其他Clang功能的交互
- 支持的平台和架构
GCC特定ASan文档:
- GCC特定标志和行为
- 与Clang实现的差异
- GCC中的平台支持
AddressSanitizer: A Fast Address Sanity Checker (USENIX Paper)
原始研究论文与技术细节:
- 影子内存算法
- 虚拟内存要求(历史16TB,现在约20TB)
- 性能基准
- 设计决策和权衡