硬件解决方案
Arm MTE(内存标记)作为Armv8.5指令集的一部分引入。MTE现在内置于Arm 最近宣布的符合Armv9 的 CPU 中,例如 Cortex-X2、Cortex-A710 和Cortex-A510。未来基于Armv9 的 CPU 也将集成 MTE。
内存标记背后的想法非常简单:将一些位(4bit)添加到内存块中,以标识应用程序对内存的使用是安全的。Arm将内存标记实现为两阶段系统,也称为锁和钥匙:
- 地址标记。在进程中的每个指针的顶部添加四bit校验位(59-56)。地址标记仅适用于 64 位应用程序,因为它使用ARM64 feature TBI (top-byte-ignore)。地址标签充当虚拟“key”。
- 内存标记。内存标签也由四位组成,在应用程序使用的物理内存中16字节对齐。这四bit不用于应用程序数据,而是单独存储。内存标签是“lock”。
虚拟地址标签(key)必须与内存标签(lock)匹配。否则,会发生错误。
软件解决方案
基于软件的解决方案,包括Address Sanitizer (Asan)和HWASAN,通过将内存损坏检测集成到现代编译器中来帮助缓解这些内存问题。但是Asan/HWASAN需要向应用程序代码添加软件工具,这会显着减慢应用程序运行时间并增加内存使用量,尤其是在移动和嵌入式系统中但由于明显的开销,它们在当前硬件上的适用性受到限制。
MTE 底层架构
Armv8.5 和 v9 实现了一种新的内存类型,Arm 将其称为 Normal Tagged Memory。CPU可以通过将地址标签与相应的内存标签进行比较来确定内存访问的安全性。开发人员可以选择标签不匹配是否导致同步异常或异步报告,从而允许应用程序继续。
异步方式将把不匹配的key和lock 记录系统寄存器中。操作系统可以隔离把这些异常,并可以根据当前执行运行的程序来决策是否采用特定异常线程来执行。
同步异常直接处理key和lock 不匹配的指令和数据。
Arm 向指令集中添加了各种新指令,用于操作tag、处理指针和堆栈标记,以及供low-level的系统使用。
MTE 在硬件中处理;加载和存储指令已被修改以验证地址标签与内存标签匹配,硬件内存分配确保地址和内存标签创建的随机化。这对操作系统开发人员和最终用户应用程序程序员有不同的影响。
Arm 增强了其AMBA 5 相干互连以支持MTE。标签检查逻辑通常内置于系统级缓存中,标签检查和标签缓存发生在DRAM 接口之前。
必须修改操作系统才能完全支持MTE。Arm 最初通过创建实现标签的 Linux 内核版本来构建 MTE 原型。Android也supportMTE。
假设操作系统支持MTE,最终用户应用程序开发人员会更容易一些。由于MTE 发生在操作系统和硬件的幕后,应用程序不需要修改源代码。堆内存的MTE 标记不需要额外的努力。但是,使用堆栈内存在现有运行时标记内存需要编译器支持,因此需要重新编译现有二进制文件。
MTE Linux Kernel
Linux 内核对 MTE 的支持目前正在Linux 内核上开发
内核 ABI 已被修改为允许在指针的顶部字节(“顶部字节忽略”或 TBI)中传递标记。Linux 现在要求用户空间在当前 ABI(将用户空间指针传递给内核时要求最高字节为 0)和传递标记指针之间进行选择。软件 HWASAN 功能和 Arm MTE 都需要对宽松 ABI 的支持,并且已在内核 v5.4 中合并。文档在这里:https : //www.kernel.org/doc/html/latest/arm64/tagged-address-abi.html
此处正在积极开发对MTE 用户空间的 arm64 内核支持
https://lore.kernel.org/linux-arm-kernel/20200703153718.16973-1-catalin.marinas@arm.com/
MTE in android
Android 11 (R) 中已经使用了TBI,详见https://source.android.com/devices/tech/debug/tagged-pointers
Android memory的分配采用支持由scudo来supportMTE.
scudo: Add initial memorytagging support.
When the hardware and operating system support the ARM Memory Tagging Extension, tag primary allocation granules with a random tag. The granules either side of the allocation are tagged with tag 0, which is normally excluded from the set of tags that may be selected randomly. Memory is also retagged with a random tag when it is freed, and we opportunistically reuse the new tag when the block is reused to reduce overhead. This causes linear buffer overflows to be caught deterministically and non-linear buffer overflows and use-after-free to be caught probabilistically.
This feature is currently only enabled for the Android allocator and depends on an experimental Linux kernel branch available here: https://github.com/pcc/linux/tree/android-experimental-mte
All code that depends on the kernel branch is hidden behind a macro, ANDROID_EXPERIMENTAL_MTE. This is the same macro that is used by the Android platform and may only be defined in non-production configurations. When the userspace interface is finalized the code will be updated to use the stable interface and all #ifdef ANDROID_EXPERIMENTAL_MTE will be removed.
堆栈标记已在clang(LLVM C 编译器)中实现,并在LLVM 9.0 中可用