内存错误调试和缓解

Android 支持多种用于调试内存错误的工具。每种工具都有其权衡,因此请阅读以下内容以确定哪种工具最适合您的用例。本文档概述了可用的工具,以便您可以决定进一步调查哪些工具,但它旨在简洁明了,因此请阅读特定工具的文档以获取详细信息。

tl;dr

  • 尽可能使用 内存安全语言 使内存错误成为不可能。
  • 始终使用 PAC/BTI 来缓解 ROP/JOP 攻击。
  • 始终使用 GWP-ASan 检测生产环境中的罕见内存错误。
  • 使用 HWASan 在测试期间检测内存错误。
  • MTE 功能的设备在 2023 年通常不可用,但如果您能够在生产环境中检测错误,请使用它。
  • 仅在测试期间将 ASan 作为最后手段使用。

内存安全语言

内存安全语言是完全避免和缓解内存错误的唯一方法。此页面上的其他工具可以帮助您使您的非内存安全代码更安全、更可靠,但使用内存安全语言可以消除整类问题。

Android 官方支持的内存安全语言是 Java 和 Kotlin。大多数 Android 应用更容易使用其中一种语言进行开发。

也就是说,有一些应用开发者发布了用 Rust 编写的代码,如果您正在阅读此页面,您可能确实有充分的理由需要原生代码(可移植性、性能或两者兼而有之)。Rust 是 Android 上内存安全原生代码的最佳选择。如果您选择此路径,NDK 团队不一定能够帮助您解决您遇到的问题,但我们很乐意 了解这些问题

PAC/BTI

指针认证和分支目标识别,也称为 PAC/BTI,是在生产环境中使用的缓解工具。虽然是两种独立的技术,但它们由相同的编译器标志控制,因此始终一起使用。

这些功能与不支持它们的设备向后兼容,因为在较早的设备上使用的新的指令是无操作指令。还需要一个足够新的内核和一个足够新的操作系统版本。在 /proc/cpuinfo 中查找 pacabti 会显示您是否有足够新的硬件和足够新的内核。Android 12 (API 31) 具有必要的用户空间支持。

优点

  • 可以在所有构建中启用,而不会在旧设备或内核上造成问题(但请确保您确实在确实支持它的设备/内核/操作系统组合上进行了测试!)

缺点

  • 仅适用于 64 位应用
  • 在不支持它的设备上不会缓解错误
  • 代码大小开销为 1%

GWP-Asan

GWP-ASan 可用于检测现场的内存错误,但采样率太低,无法成为有效的缓解措施。

优点

  • 没有明显的 CPU 或内存开销
  • 易于部署:不需要重新构建原生代码
  • 适用于 32 位应用

缺点

  • 低采样率需要大量用户才能有效地查找错误
  • 仅检测堆错误,不检测堆栈错误

HWASan

硬件地址消毒器,也称为 HWASan,是测试期间捕获内存错误的最佳选择。它在与自动化测试一起使用时最有用,尤其是在运行模糊测试时,但根据您应用的性能需求,它也可能在高级手机的试用环境中使用。

优点

  • 没有误报
  • 检测 ASan 无法检测的其他类别的错误(返回后使用堆栈)
  • 假阴性率低于 MTE(1/256 对 1/16)
  • 内存开销低于 ASan,其最接近的替代方案

缺点

  • CPU(约 100%)、代码大小(约 50%)和内存(10% - 35%)开销显着
  • 在 API 34 和 NDK r26 之前,需要刷入兼容 HWASan 的镜像
  • 仅适用于 64 位应用

MTE

内存标记扩展,也称为 MTE,是 HWASan 的一种成本更低的替代方案。除了调试和测试功能外,它还可用于检测和缓解生产环境中的内存损坏。如果您拥有用于测试 MTE 构建的硬件,则应启用它。

优点

  • 对于许多应用来说,其开销足够低,可以在生产环境中容忍
  • 没有误报
  • 无需重新构建代码即可检测堆错误(但检测栈错误需要重新构建)

缺点

  • 截至 2024 年,尚无默认启用 MTE 的商用设备,但 Arm 的文档解释了如何在 Pixel 8/Pixel 8 Pro 上启用 MTE 进行测试
  • 假阴性率为 1/16,而 HWASan 的假阴性率为 1/256
  • 仅适用于 64 位应用
  • 需要为目标 MTE 启用和未启用 MTE 的设备构建单独的库

ASan

地址消毒器,也称为 ASan,是可用工具中最古老且最广泛使用的工具。它有助于在测试期间捕获内存错误并调试仅影响旧设备的问题,而这些旧设备上没有其他工具可用。**只要可能,请优先使用 HWASan。**

优点

  • 广泛可用。可能适用于 KitKat 及更高版本的设备
  • 正确使用时,不会出现假阳性或假阴性

缺点

  • 难以正确构建和打包
  • 所有选项中开销最高:CPU 占用率约 100%,代码大小约 50%,内存使用率约 100%
  • 不再受支持
  • 存在已知的错误,这些错误不会被修复