内存错误调试和缓解

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)是在生产环境中使用的缓解工具。尽管是单独的技术,但它们由相同的编译器标志控制,因此始终一起使用。

这些功能与不支持它们的设备向后兼容,因为使用的新的指令在较早的设备上是空操作。还需要更新的内核和更新版本的 OS。在/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%
  • 不再支持
  • 存在已知的不会修复的错误