MemTagSanitizer¶
简介¶
注意: 本页介绍的是一个正在开发中的工具。部分功能正在计划中但尚未实现。截至 2019 年 10 月,尚不存在能够运行 MemTagSanitizer 的硬件。
MemTagSanitizer 是一款快速内存错误检测器,也是基于 Armv8.5-A 内存标记扩展 的 代码加固工具。它检测的错误类型与 AddressSanitizer 或 HardwareAssistedAddressSanitizer 类似,但开销低得多。
MemTagSanitizer 的开销预计在个位数百分比范围内,包括 CPU 和内存。计划推出调试模式,该模式具有稍高的内存开销和更好的诊断功能。MemTagSanitizer 的主要用例是在生产二进制文件中进行代码加固,预计它将成为堆栈和基于堆的内存错误的有力缓解措施。
用法¶
使用 -fsanitize=memtag
标志编译和链接您的程序。这仅在以具有内存标记扩展的 AArch64 Android 为目标时有效。一种可能的实现方法是将 --target=aarch64-linux-android -march=armv8+memtag
添加到您的编译标志中。
请注意,这样做会覆盖相同类型的现有标志。假设您已经以 AArch64 Android 为目标,另一种方法是将 -Xclang -target-feature -Xclang +mte
添加到您的编译标志中。这会添加内存标记功能,而不会更改任何其他内容。
实现¶
有关基于标记的内存安全方法的概述,请参阅 HardwareAssistedAddressSanitizer。MemTagSanitizer 遵循类似的实现策略,但标记存储(影子)由硬件提供。
MTE 硬件功能的快速概述
每 16 字节对齐的内存可以分配一个 4 位分配标签 (Allocation Tag)。
每个指针都可以有一个 4 位地址标签 (Address Tag),该标签位于其最高有效字节中。
如果地址标签 (Address Tag) != 分配标签 (Allocation Tag),则大多数内存访问指令会生成异常。
提供了用于快速标签操作的特殊指令。
栈插桩¶
基于堆栈的内存错误通过以下方式检测:在其生命周期开始时,将每个局部变量的分配标签 (Allocation Tag) 更新为随机值,并在其生命周期结束时将其重置为堆栈指针的地址标签 (Address Tag)。未分配的堆栈空间应与 SP 的地址标签 (Address Tag) 匹配;这允许在可以静态证明内存安全时跳过任何变量的标记。
为大型函数中的每个堆栈变量分配真正随机的标签可能会导致显着的代码大小开销,因为这意味着每个变量的地址都是独立的、不可重物化的值;因此,具有 N 个局部变量的函数将在其生命周期的大部分时间里保持额外的 N 个活动值。
因此,MemTagSanitizer 每个函数最多生成一个随机标签,称为“基本标签”。其他堆栈变量(如果有)则在相对于基本标签的固定偏移量处分配标签。
有关堆栈插桩的更多详细信息,请参阅本文档。
堆标记¶
注意: 截至 2019 年 10 月,此部分尚未实现。
MemTagSanitizer 将使用 Scudo Hardened Allocator,并添加额外代码以在以下情况下更新内存标记:
从系统获得新内存时。
分配被释放时。
只要返回具有匹配地址标签 (Address Tag) 的指针,就无需更改 malloc() 中大部分已分配内存的分配标签 (Allocation Tag)。