模糊测试 ASan 模式下有关堆的 ASan Redzone 和 Check 的源码解析
字数 1315 2025-08-22 12:22:37
AddressSanitizer (ASan) 深度解析:堆内存保护机制
1. ASan 概述
1.1 模糊测试与 ASan 的关系
模糊测试(Fuzz Testing)是一种自动化软件测试技术,通过向程序提供随机或异常输入来检测漏洞与崩溃。ASan 在模糊测试中扮演关键角色,能够有效捕捉以下内存问题:
- 缓冲区溢出
- 内存越界
- 未初始化内存使用
- 内存泄漏
1.2 ASan 核心机制
ASan 通过以下技术实现内存错误检测:
- 编译时插桩:在内存操作前后插入检查代码
- 运行时检查:动态验证内存访问合法性
- 影子内存(Shadow Memory):1:8 比例映射主内存状态
- Redzone:在内存块周围设置不可访问区域
2. ASan 工作原理详解
2.1 内存区域划分
ASan 将内存分为两类区域:
- 可访问区域:正常分配的内存空间
- 不可访问区域(Redzone):内存块两侧的填充区域,用于检测越界访问
2.2 影子内存机制
影子内存是 ASan 的核心数据结构,具有以下特点:
- 比例:1 字节影子内存对应 8 字节主内存
- 标记含义:
0x00:8 字节全部可访问0x01-0x07:前 n 个字节可访问0xF1:Redzone 区域,不可访问
2.3 典型错误检测流程
- 每次内存访问前检查影子内存状态
- 若访问地址在 Redzone 或已释放区域,触发错误报告
- 提供详细的错误信息(错误类型、调用栈等)
3. 堆内存保护机制
3.1 Redzone 标记机制
3.1.1 malloc 时的 Redzone 设置
当调用 malloc 时,ASan 会:
- 分配比请求更大的内存块(包含 Redzone)
- 在影子内存中标记:
- 用户可用区域为可访问
- Redzone 区域为不可访问
关键源码 (asan_allocator.cpp):
void* Allocate(uptr size, uptr alignment, BufferedStackTrace* stack,
AllocType alloc_type, bool can_fill) {
// 设置左侧 Redzone
PoisonShadow(alloc_beg, user_beg - alloc_beg, kAsanHeapLeftRedzoneMagic);
// 设置右侧 Redzone
PoisonShadow(tail_beg, tail_end - tail_beg, kAsanHeapLeftRedzoneMagic);
// 标记用户可用区域
if (size_rounded_down_to_granularity)
PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
}
3.1.2 free 时的内存标记
释放内存时,ASan 会:
- 将内存放入隔离区(quarantine)
- 毒化影子内存,标记为已释放状态
关键源码:
void PreQuarantine(AsanChunk* m) const {
// 毒化已释放内存
PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), ASAN_SHADOW_GRANULARITY),
kAsanHeapFreeMagic);
}
3.2 堆内存检查机制
3.2.1 越界访问检测
ASan 在每次堆内存操作前插入检查代码,例如:
if (*((ptr >> 3) + 0x7FFF8000) != 0 &&
((ptr & 7) + 3) >= *((ptr >> 3) + 0x7FFF8000))
__asan_report_store4(ptr);
对应的汇编实现 (asan_rtl_x86_64.S):
ASAN_MEMORY_ACCESS_CALLBACK_ADD_4(reg, op):
mov %##reg,%r10
shr $0x3,%r10
movsbl ASAN_SHADOW_OFFSET_CONST(%r10),%r10d
test %r10d,%r10d
jne .check_reg_op_4_add
retq
.check_reg_op_4_add:
mov %##reg,%r11
and $0x7,%r11d
add $0x3,%r11d
cmp %r10d,%r11d
jl .return_reg_op_4_add
mov %##reg,%rdi
jmp __asan_report_op4_asm
3.2.2 内存泄漏检测
ASan 与 LeakSanitizer(LSan) 协作检测内存泄漏:
- 记录所有内存分配的堆栈信息
- 程序结束时扫描未释放的内存块
- 生成泄漏报告,包括:
- 泄漏内存大小
- 分配位置(文件、行号)
- 调用堆栈
关键源码 (asan_memory_profile.cpp):
void Print(uptr top_percent, uptr max_number_of_contexts) {
Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
"%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
"showing top %zd%% (at most %zd unique contexts)\n",
total_allocated_user_size_,
total_allocated_count_,
total_quarantined_user_size_,
total_quarantined_count_,
total_other_count_,
total_allocated_count_ + total_quarantined_count_ + total_other_count_,
top_percent,
max_number_of_contexts);
}
4. 实践应用
4.1 编译选项
常用 ASan 编译选项:
# 基本内存错误检测
clang/gcc -fsanitize=address
# 内存泄漏检测
clang/gcc -fsanitize=leak
# 未初始化内存检测
clang/gcc -fsanitize=memory
# 未定义行为检测
clang/gcc -fsanitize=undefined
# 线程安全检测
clang/gcc -fsanitize=thread
4.2 典型错误输出示例
堆溢出错误:
ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 4 at 0x60200000effc thread T0
#0 0x400a96 in main /path/to/file.c:10
使用后释放错误:
ERROR: AddressSanitizer: heap-use-after-free
READ of size 4 at 0x60200000eff0 thread T0
#0 0x400a96 in main /path/to/file.c:12
内存泄漏错误:
ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f15f5897887 in __interceptor_malloc
#1 0x56541800e1be in main /root/work/Asan/memory_leak.c:5
5. 总结
ASan 通过以下机制提供强大的内存错误检测能力:
- 编译时插桩:在内存操作点插入检查代码
- 影子内存:高效跟踪内存状态
- Redzone:边界保护防止越界
- 隔离区:延迟释放检测 UAF
- 泄漏检测:与 LSan 协作扫描未释放内存
这些机制使 ASan 成为模糊测试和日常开发中不可或缺的内存错误检测工具。