相关文章推荐
豪情万千的眼镜  ·  黄鸟玉兰_百度百科·  5 月前    · 
好帅的抽屉  ·  【冰点】:我们是主角·  10 月前    · 
踢足球的洋葱  ·  Amazon Live·  1 年前    · 

AddressSanitizer 运行时库拦截常见的内存分配函数和操作,以检查内存访问。 有几个不同的运行时库可支持编译器可能生成的各种类型的可执行文件。 只要在编译时传递 /fsanitize=address 选项,编译器和链接器就会自动链接相应的运行时库。 可以在链接时使用 /NODEFAULTLIB 选项重写默认行为。 有关详细信息,请参阅 AddressSanitizer 语言、生成和调试参考 中有关 链接 的部分。

下面是用于链接到 AddressSanitizer 运行时的运行时库清单,其中 {arch} i386 x86_64

这些库保留了体系结构名称的 Clang 约定。 MSVC 约定通常是 x86 和 x64,而不是 i386 和 x86_64。 它们引用相同的体系结构。

使用 cl /fsanitize=address 进行编译时,编译器会生成指令来管理和检查 影子字节 。 程序使用此检测来检查堆栈、堆或全局范围内的内存访问。 编译器还会生成描述堆栈和全局变量的元数据。 此元数据使运行时能够生成精确的错误诊断:源代码中的函数名称、行和列。 将编译器检查和运行时库相结合,可以在运行时遇到多种类型的 内存安全 bug 时进行准确诊断。

AddressSanitizer 通过多种热修补技术实现函数拦截。 源代码本身对这些技术进行了最好的记录

运行时库拦截许多常见的内存管理和内存操作函数。 有关列表,请参阅 AddressSanitizer 拦截函数列表 。 分配拦截器管理与每个分配调用相关的元数据和影子字节。 每次调用 CRT 函数(例如 malloc delete )时,拦截器都会在 AddressSanitizer 影子内存区域中设置特定值,以指示这些堆位置当前是否可访问以及分配的边界是什么。 这些影子字节允许编译器生成对 影子字节 的检查,以确定负载或存储是否有效。

无法保证拦截成功。 如果函数序言太短而无法写入 jmp ,则拦截可能会失败。 如果拦截失败,程序将引发 debugbreak 并停止。 如果附加一个调试器,则可以清楚了解拦截问题的原因。 如果遇到此问题,请 报告 bug

用户可以选择尝试在拦截失败后继续执行,方法是将环境变量 ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE 设置为任意值。 在拦截失败后继续执行可能会导致丢失该函数的 bug 报告。

自定义分配器和 AddressSanitizer 运行时

AddressSanitizer 运行时为常见的分配器接口 malloc / free new / delete HeapAlloc / HeapFree (通过 RtlAllocateHeap / RtlFreeHeap )提供拦截器。 许多程序会出于某种原因使用自定义分配器,例如使用 dlmalloc 的任何程序或使用 std::allocator 接口和 VirtualAlloc() 的解决方案。 编译器无法自动将影子内存管理调用添加到自定义分配器。 用户有责任使用 提供的手动中毒接口 。 此 API 使这些分配器能够与现有的 AddressSanitizer 运行时和 影子字节 约定一起正常运行。

手动 AddressSanitizer 中毒接口

启蒙的接口很简单,但它对用户施加了对齐限制。 用户可以通过导入 sanitizer/asan_interface.h 来导入这些原型。 以下是接口函数原型:

void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);

为方便起见,AddressSanitizer 接口头文件提供了包装器宏。 这些宏检查在编译期间是否启用了 AddressSanitizer 功能。 它们允许源代码在不需要时省略中毒函数调用。 这些宏应该优先于直接调用上述函数:

#define ASAN_POISON_MEMORY_REGION(addr, size)
#define ASAN_UNPOISON_MEMORY_REGION(addr, size)

AddressSanitizer 中毒的对齐要求

影子字节的任何手动中毒都必须考虑对齐要求。 如有必要,用户必须添加填充,以便影子字节在影子内存中的字节边界处结束。 AddressSanitizer 影子内存中的每个位对应用程序内存中单个字节的状态进行编码。 这种编码意味着每个分配(包括任何填充)的总大小必须与 8 字节边界对齐。 如果不满足对齐要求,可能会生成 bug 报告。 不正确的报告可能表现为缺少报告(漏报)或非错误报告(误报)。

有关对齐要求和潜在问题的说明,请参阅提供的 ASan 对齐示例。 一个是一个小程序,显示手动影子内存中毒可能会出错的情况。 第二个是使用 std::allocator 接口的手动中毒的示例实现。

运行时选项

Microsoft C/C++ (MSVC) 使用基于 llvm 项目存储库中的 Clang AddressSanitizer 运行时的运行时。 因此,大多数运行时选项在两个版本之间共享。 此处提供了公共 Clang 运行时选项的完整列表。 以下各部分介绍了其中一些差异。 如果发现某选项无法按预期运行,请报告 bug

不支持的 AddressSanitizer 选项

  • detect_container_overflow
  • unmap_shadow_on_exit
  • AddressSanitizer 运行时选项 halt_on_error 无法按预期方式运行。 在 Clang 和 MSVC 运行时库中,许多错误类型被视为不可持续,包括大多数内存损坏错误。

    有关详细信息,请参阅与 Clang 12.0 的差异部分。

    特定于 MSVC 的 AddressSanitizer 运行时选项

  • windows_hook_legacy_allocators布尔值,设置为 false 以禁用和LocalAlloc分配器的拦截GlobalAlloc

    撰写本文时,公共 llvm 项目运行时中没有 windows_hook_legacy_allocators 选项。 该选项最终可能会进入公共项目;但需要经过代码审查和社区接受。

    选项 windows_hook_rtl_allocators 以前是一个可选功能,因为 AddressSanitizer 是试验性的,但现在默认启用。 在 Visual Studio 2022 版本 17.4.6 之前的版本中,默认选项值为 false。 在 Visual Studio 2022 版本 17.4.6 及更高版本中,选项 windows_hook_rtl_allocators 默认为 true

  • iat_overwrite 字符串,默认情况下设置为 "error" 。 其他可能的值为 "protect""ignore"。 某些模块可能会覆盖 import address table 其他模块的 ,以自定义某些函数的实现。 例如,驱动程序通常为特定硬件提供自定义实现。 选项 iat_overwrite 管理 AddressSanitizer 运行时针对特定 memoryapi.h 函数的覆盖的保护。 运行时当前跟踪 VirtualAllocVirtualProtectVirtualQuery 函数以保护。 此选项在 Visual Studio 2022 版本 17.5 预览版 1 及更高版本中可用。 以下 iat_overwrite 值控制覆盖受保护函数时运行时的反应方式:

  • 如果设置为 "error" (默认) ,则每当检测到覆盖时,运行时将报告错误。
  • 如果设置为 "protect",运行时会尝试避免使用覆盖的定义并继续操作。 实际上,函数的原始 memoryapi 定义是从运行时内部使用的,以避免无限递归。 进程中的其他模块仍使用覆盖的定义。
  • 如果设置为 "ignore",运行时不会尝试更正任何覆盖的函数,并继续执行。
  • windows_fast_fail_on_error 默认情况下,boolean (false) ,设置为 true 以允许进程在打印错误报告后以__fastfail (71) 终止。

    当 abort_on_error 值设置为 true 时,在 Windows 上,程序终止并退出 (3) 。 为了不更改当前行为,我们决定改为引入此新选项。 如果abort_on_error和windows_fast_fail_on_error都为 true,则程序将退出__fastfail。

    AddressSanitizer 拦截函数列表 (Windows)

    AddressSanitizer 运行时热修补许多函数,以在运行时启用内存安全检查。 下面是 AddressSanitizer 运行时监视的函数的非详尽列表。

    默认拦截器

  • __C_specific_handler(仅限 x64)
  • _aligned_free
  • _aligned_malloc
  • _aligned_msize
  • _aligned_realloc
  • _calloc_base
  • _calloc_crt
  • _calloc_dbg(仅限调试运行时)
  • _except_handler3(仅限 x86)
  • _except_handler4(仅限 x86)(未记录)
  • _expand
  • _expand_base(未记录)
  • _expand_dbg(仅限调试运行时)
  • _free_base(未记录)
  • _free_dbg(仅限调试运行时)
  • _malloc_base(未记录)
  • _malloc_crt(未记录)
  • _malloc_dbg(仅限调试运行时)
  • _msize
  • _msize_base(未记录)
  • _msize_dbg(仅限调试运行时)
  • _realloc_base(未记录)
  • _realloc_crt(未记录)
  • _realloc_dbg(仅限调试运行时)
  • _recalloc
  • _recalloc_base(未记录)
  • _recalloc_crt(未记录)
  • _recalloc_dbg(仅限调试运行时)
  • _strdup
  • calloc
  • CreateThread
  • frexp
  • longjmp
  • malloc
  • memchr
  • memcmp
  • memcpy
  • memmove
  • memset
  • RaiseException
  • realloc
  • RtlAllocateHeap
  • RtlCreateHeap
  • RtlDestroyHeap
  • RtlFreeHeap
  • RtlRaiseException
  • RtlReAllocateHeap(未记录)
  • RtlSizeHeap(未记录)
  • SetUnhandledExceptionFilter
  • strcat
  • strchr
  • strcmp
  • strcpy
  • strcspn
  • strdup
  • strlen
  • strncat
  • strncmp
  • strncpy
  • strnlen
  • strpbrk
  • strspn
  • strstr
  • strtok
  • strtol
  • wcslen
  • wcsnlen
  • 可选拦截器

    仅当启用了 AddressSanitizer 运行时选项时,才会安装此处列出的拦截器。 设置为 windows_hook_legacy_allocatorsfalse 禁用旧版分配器拦截。 set ASAN_OPTIONS=windows_hook_legacy_allocators=false

  • GlobalAlloc
  • GlobalFree
  • GlobalHandle
  • GlobalLock
  • GlobalReAlloc
  • GlobalSize
  • GlobalUnlock
  • LocalAlloc
  • LocalFree
  • LocalHandle
  • LocalLock
  • LocalReAlloc
  • LocalSize
  • LocalUnlock
  • AddressSanitizer 概述
    AddressSanitizer 已知问题
    AddressSanitizer 生成和语言参考
    AddressSanitizer 影子字节
    AddressSanitizer 云或分布式测试
    AddressSanitizer 调试程序集成
    AddressSanitizer 错误示例