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