AddressSanitizer 影子字节
我们简要总结了影子字节的概念以及 /fsanitize=address
的运行时实现如何使用它们。 有关更多详细信息,请参阅初始研究 AddressSanitizer - Serebryany 等和当前的 AddressSanitizer 算法文档。
核心概念
应用程序的虚拟地址空间中的每 8 个字节都可以使用一个影子字节来描述。
一个影子字节描述当前可访问的字节数,如下所示:
- 0 表示所有 8 个字节
- 1-7 表示 1 到 7 个字节
- 负数为运行时编码上下文,用于报告诊断。
影子字节图例
请考虑定义所有负数的影子字节图例:
映射 - 描述地址空间
应用程序的虚拟地址空间中与“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 错误示例