模糊测试 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 通过以下技术实现内存错误检测:

  1. 编译时插桩:在内存操作前后插入检查代码
  2. 运行时检查:动态验证内存访问合法性
  3. 影子内存(Shadow Memory):1:8 比例映射主内存状态
  4. Redzone:在内存块周围设置不可访问区域

2. ASan 工作原理详解

2.1 内存区域划分

ASan 将内存分为两类区域:

  • 可访问区域:正常分配的内存空间
  • 不可访问区域(Redzone):内存块两侧的填充区域,用于检测越界访问

2.2 影子内存机制

影子内存是 ASan 的核心数据结构,具有以下特点:

  • 比例:1 字节影子内存对应 8 字节主内存
  • 标记含义:
    • 0x00:8 字节全部可访问
    • 0x01-0x07:前 n 个字节可访问
    • 0xF1:Redzone 区域,不可访问

2.3 典型错误检测流程

  1. 每次内存访问前检查影子内存状态
  2. 若访问地址在 Redzone 或已释放区域,触发错误报告
  3. 提供详细的错误信息(错误类型、调用栈等)

3. 堆内存保护机制

3.1 Redzone 标记机制

3.1.1 malloc 时的 Redzone 设置

当调用 malloc 时,ASan 会:

  1. 分配比请求更大的内存块(包含 Redzone)
  2. 在影子内存中标记:
    • 用户可用区域为可访问
    • 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 会:

  1. 将内存放入隔离区(quarantine)
  2. 毒化影子内存,标记为已释放状态

关键源码:

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) 协作检测内存泄漏:

  1. 记录所有内存分配的堆栈信息
  2. 程序结束时扫描未释放的内存块
  3. 生成泄漏报告,包括:
    • 泄漏内存大小
    • 分配位置(文件、行号)
    • 调用堆栈

关键源码 (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 通过以下机制提供强大的内存错误检测能力:

  1. 编译时插桩:在内存操作点插入检查代码
  2. 影子内存:高效跟踪内存状态
  3. Redzone:边界保护防止越界
  4. 隔离区:延迟释放检测 UAF
  5. 泄漏检测:与 LSan 协作扫描未释放内存

这些机制使 ASan 成为模糊测试和日常开发中不可或缺的内存错误检测工具。

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 ): 3.1.2 free 时的内存标记 释放内存时,ASan 会: 将内存放入隔离区(quarantine) 毒化影子内存,标记为已释放状态 关键源码: 3.2 堆内存检查机制 3.2.1 越界访问检测 ASan 在每次堆内存操作前插入检查代码,例如: 对应的汇编实现 ( asan_rtl_x86_64.S ): 3.2.2 内存泄漏检测 ASan 与 LeakSanitizer(LSan) 协作检测内存泄漏: 记录所有内存分配的堆栈信息 程序结束时扫描未释放的内存块 生成泄漏报告,包括: 泄漏内存大小 分配位置(文件、行号) 调用堆栈 关键源码 ( asan_memory_profile.cpp ): 4. 实践应用 4.1 编译选项 常用 ASan 编译选项: 4.2 典型错误输出示例 堆溢出错误: 使用后释放错误: 内存泄漏错误: 5. 总结 ASan 通过以下机制提供强大的内存错误检测能力: 编译时插桩 :在内存操作点插入检查代码 影子内存 :高效跟踪内存状态 Redzone :边界保护防止越界 隔离区 :延迟释放检测 UAF 泄漏检测 :与 LSan 协作扫描未释放内存 这些机制使 ASan 成为模糊测试和日常开发中不可或缺的内存错误检测工具。