AddressSanitizer 影子字节

我们简要总结了影子字节的概念以及 /fsanitize=address 的运行时实现如何使用它们。 有关更多详细信息,请参阅开创性论文AddressSanitizer 算法

核心概念

应用程序的虚拟地址空间中的每 8 个字节都可以使用一个影子字节来描述

一个影子字节描述当前可访问的字节数,如下所示:

  • 0 表示所有 8 个字节
  • 1-7 表示 1 到 7 个字节
  • 负数为运行时编码上下文,用于报告诊断。

影子字节图例

请考虑定义所有负数的影子字节图例:

Screenshot of the AddressSanitizer shadow-byte legend.

映射 - 描述地址空间

应用程序的虚拟地址空间中与“0-mod-8”对齐的每 8 个字节都可以映射到描述虚拟地址空间中该插槽的影子字节。 可以通过简单的班次和添加来实现此映射

在 x86 上:

char shadow_byte_value = *((Your_Address >> 3) + 0x30000000)

在 x64 上:

char shadow_byte_value = *((Your_Address >> 3) + _asan_runtime_assigned_offset)

代码生成 - 测试

请考虑如何通过编译器生成的代码、静态数据或运行时写入特定的影子字节。 此伪代码显示了如何在任何加载或存储之前生成检查:

ShadowAddr = (Addr >> 3) + Offset;
if (*ShadowAddr != 0) {
    ReportAndCrash(Addr);
}

检测小于 8 字节的内存引用时,检测会稍微复杂一些。 如果影子值为正(意味着只能访问 8 字节字中的前 k 个字节),我们需要将地址的后 3 位与 k 进行比较。

ShadowAddr = (Addr >> 3) + Offset;
k = *ShadowAddr;
if (k != 0 && ((Addr & 7) + AccessSize > k)) {
    ReportAndCrash(Addr);
}

运行时和编译器生成的代码都写入影子字节。 当作用域结束或存储释放时,这些影子字节可以授予或撤销访问权限。 上述检查读取用于(在程序执行中的某个时间)描述应用程序地址空间中的 8 字节“槽”的影子字节。 除了这些显式生成的检查之外,在运行时在 CRT 中拦截(或“挂钩”)许多函数后,它还会检查影子字节。

有关详细信息,请参阅已拦截函数的列表。

设置影子字节

编译器生成的代码和 AddressSanitizer 运行时都可以写入影子字节。 例如,编译器可以设置影子字节,以允许对内部范围内定义的堆栈局部变量进行固定范围访问。 运行时可以用影子字节包围数据部分中的全局变量。

另请参阅

AddressSanitizer 概述
AddressSanitizer 已知问题
AddressSanitizer 生成和语言参考
AddressSanitizer 运行时参考
AddressSanitizer 云或分布式测试
AddressSanitizer 调试程序集成
AddressSanitizer 错误示例