从 Android 11 开始,对于 64 位进程,所有堆分配都在指针的顶字节中设置了实现定义的标记(在内核支持 ARM Top-byte Ignore (TBI) 的设备上)。任何修改此标记的应用都会在取消分配期间检查标记时终止。这对于未来支持 ARM 内存标记扩展 (MTE) 的硬件是必需的。
顶字节忽略
ARM 的顶字节忽略功能适用于所有 Armv8 AArch64 硬件中的 64 位代码。此功能意味着硬件在访问内存时会忽略指针的顶字节。
TBI 需要一个兼容内核,该内核可以正确处理从用户空间传递的标记指针。来自 4.14 (Pixel 4) 及更高版本的 Android Common Kernels 具有所需的 TBI 补丁。
在内核中支持 TBI 的设备会在进程启动时动态检测到,并将实现相关的标记插入到所有堆分配的指针的顶字节中。在此之后,将运行检查以确保在取消分配内存时标记未被截断。
内存标记扩展就绪状态
ARM 的内存标记扩展 (MTE) 有助于解决内存安全问题。MTE 的工作原理是标记堆栈、堆和全局内存上每个内存分配的第 56-59 位地址位。硬件和指令集会自动检查每次内存访问时是否使用了正确的标记。
在指针的顶字节中错误地存储信息的 Android 应用保证会在启用 MTE 的设备上崩溃。标记指针使得在 MTE 设备可用之前更容易检测和拒绝指针顶字节的不正确使用。
开发者支持
如果您的应用崩溃并且系统提示您访问此链接,则可能意味着以下情况之一
- 应用尝试释放系统堆分配器未分配的指针。
- 应用中的某些内容修改了指针的顶字节。指针的顶字节无法修改,您的代码需要更改以解决此问题。
指针顶字节被错误使用或修改的示例。
- 特定类型的指针在顶部的 16 个地址位中存储了应用特定的元数据。
- 指针转换为 double 类型,然后再转换回来,从而丢失了较低的地址位。
- 代码计算来自不同堆栈帧的局部变量地址之间的差异,以此来衡量递归深度。
某些应用可能依赖于在指针顶字节设置时行为不正确的库。我们认识到,快速修复这些库中的底层问题可能并非易事。因此,默认情况下,对于使用 targetSdkLevel < 30
构建的应用,不会启用指针标记。我们还为使用 targetSdkLevel >= 30
构建的应用提供了一种“逃生舱口”,以缓解过渡期。
通过将以下内容添加到您的 AndroidManifest.xml
文件来使用此“逃生舱口”
<application android:allowNativeHeapPointerTagging="false"> ... </application>
这将禁用您应用的指针标记功能。这不能解决底层的代码健康问题。此“逃生舱口”将在未来版本的 Android 中消失,因为此类问题将与 MTE 不兼容。