内存错误调试与缓解

Android 支持多种内存错误调试工具。每种工具都有其优缺点,请阅读下文以决定哪种工具最适合你的用例。本文档概述了可用的工具,以便你决定进一步研究哪种工具,但本文力求简洁,因此请阅读工具特定的文档以获取详细信息。

内容摘要

  • 只要可能,就使用内存安全语言,以完全避免内存错误
  • 始终使用 PAC/BTI 来缓解 ROP/JOP 攻击
  • 始终使用 GWP-ASan 来检测生产环境中罕见的内存错误
  • 在测试期间使用 HWASan 检测内存错误
  • 支持 MTE 的设备通常不可用。在这种情况改变之前,这将不会有所帮助
  • 仅在别无选择时才在测试期间使用 ASan

内存安全语言

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

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

话虽如此,确实有一些应用开发者交付了用 Rust 编写的代码,如果你正在阅读本页,你可能有充分的理由需要原生代码(可移植性、性能,或两者兼有)。Rust 是在 Android 上编写内存安全的原生代码的最佳选择。如果你选择这条路,NDK 团队不一定能帮助你解决遇到的问题,但我们很乐意听取相关反馈

PAC/BTI

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

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

优点

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

缺点

  • 仅适用于 64 位应用
  • 无法缓解不支持此功能的设备上的错误
  • 1% 的代码大小开销

GWP-Asan

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

优点

  • 无明显的 CPU 或内存开销
  • 部署简单:无需重新构建原生代码
  • 适用于 32 位应用

缺点

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

HWASan

硬件地址清理程序,也称为 HWASan,是测试期间捕获内存错误的最佳工具。与自动化测试结合使用时最有用,尤其是在运行模糊测试时,但根据应用的性能需求,它也可能在高端手机的内部测试环境中使用。

优点

  • 无误报
  • 检测 ASan 无法检测到的其他类别的错误(返回后使用栈)
  • 漏报率低于 MTE(1/256 vs 1/16)
  • 内存开销低于 ASan(最接近的替代工具)

缺点

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

MTE

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

优点

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

缺点

  • 截至 2024 年,没有默认启用 MTE 的商用设备,但 Arm 的文档解释了如何在 Pixel 8/Pixel 8 Pro 上启用 MTE 进行测试
  • 漏报率为 1/16,而 HWASan 为 1/256
  • 仅适用于 64 位应用
  • 需要为同时支持 MTE 和不支持 MTE 的设备构建单独的库

ASan

地址清理程序,也称为 ASan,是可用工具中最老、最广泛可用的。它在测试期间捕获内存错误以及调试仅影响旧设备(其中没有其他可用工具)的问题时非常有用。只要可能,请优先使用 HWASan。

优点

  • 广泛可用。可能适用于像 KitKat 一样旧的设备
  • 正确使用时没有误报或漏报

缺点

  • 难以正确构建和打包
  • 所有选项中开销最高:~100% CPU、~50% 代码大小、~100% 内存使用
  • 不再受支持
  • 存在已知 bug,且不会修复