HWAddressSanitizer

如需了解如何读取 HWASan 崩溃,请参阅了解 HWASan 报告

硬件辅助的 AddressSanitizer (HWASan) 是一款类似于 AddressSanitizer 的内存错误检测工具。与 ASan 相比,HWASan 使用的内存少得多,因而更适合用于整个系统的清理。HWASan 仅适用于 Android 10 及更高版本,且只能用于 AArch64 硬件。

虽然 HWASan 主要适用于 C/C++ 代码,但也有助于调试导致用于实现 Java 接口的 C/C++ 代码崩溃的 Java 代码。HWASan 在这种情况下很有用,因为它能捕获到发生的内存错误,并直接将您带到导致问题发生的代码。

您可以通过 ci.android.com,将预构建的 HWASan 映像刷写到支持的 Pixel 设备上(详细设置说明)。

与传统的 ASan 相比,HWASan 具有如下特征:

  • 类似的 CPU 开销(约为 2 倍)
  • 类似的代码大小开销 (40 - 50%)
  • 更小的 RAM 开销 (10% - 35%)

HWASan 能检测到 ASan 所能检测到的同一系列错误:

  • 堆栈和堆缓冲区上溢/下溢
  • 释放之后的堆使用情况
  • 超出范围的堆栈使用情况
  • 重复释放/错误释放

此外,HWASan 还可以检测返回之后的堆栈使用情况。

HWASan(与 ASan 相同)与 UBSan 兼容,您可以对一个目标同时启用二者。

实现详情和限制

HWASan 基于内存标记方法,在这种方法中,小的随机标记值同时与指针和内存地址范围相关联。为使内存访问有效,指针和内存标记必须匹配。HWASan 依赖于 ARMv8 功能 Top-Byte-Ignore(TBI,也称为虚拟地址标记)将指针标记存储在地址的最高位。

如需详细了解 HWASan 的设计,请前往 Clang 文档所在的网站。

从设计上讲,ASan 对检测上溢规定了有限大小的红色区域,并对检测释放后使用情况规定了有限容量隔离区,而 HWASan 则没有这些规定。因此,无论上溢有多大或内存在多久之前解除分配,HWASan 都可以检测到 bug。这赋予 HWASan 比 ASan 大得多的优势。

不过,HWASan 具有有限数量的可能标记值(256 个),这意味着在一次程序执行期间忽略任何错误的概率为 0.4%。

要求

较新版本(4.14 及更高版本)的通用 Android 内核开箱即支持 HWASan。Android 10 专属分支不支持 HWASan。

Android 11 开始提供对 HWASan 的用户空间支持。

如果您使用的是其他内核,则 HWASan 要求 Linux 内核接受系统调用参数中已加标记的指针。在下列上游补丁集中实现了对此项要求的支持:

如果您要使用自定义工具链进行构建,请确保它包含直到 LLVM 提交 c336557f 的所有内容。

使用 HWASan

如需使用 HWASan 构建整个平台,请使用以下命令:

lunch aosp_walleye-userdebug # (or any other product)
export SANITIZE_TARGET=hwaddress
m -j

为方便起见,您可以向产品定义中添加 SANITIZE_TARGET 设置,与 aosp_coral_hwasan 类似。

对于熟悉 AddressSanitizer 的用户来说,构建过程中的许多复杂问题都不复存在了:

  • 无需运行两次 make。
  • 增量 build 可以直接使用。
  • 无需刷写 userdata。

此外,还取消了一些 AddressSanitizer 限制:

  • 支持静态可执行文件。
  • 可以跳过对除 libc 以外的任何目标的排错。与 ASan 不同,如果库经过排错,并不要求对与其关联的任何可执行文件也进行排错。

可以在 build 号相同(或更高)的 HWASan 和常规映像之间自由切换。无需擦除设备。

如需跳过对某个模块的排错,请使用 LOCAL_NOSANITIZE := hwaddress (Android.mk) 或 sanitize: { hwaddress: false } (Android.bp)。

对单个目标进行排错

可以在常规(未排错)build 中按目标启用 HWASan,只要 libc.so 也进行了排错即可。将 hwaddress: true 添加到 bionic/libc/Android.bp 的 "libc_defaults" 中的 sanitize 块。然后,在要处理的目标中执行相同的操作。

请注意,如果对 libc 进行排错,便会在整个系统范围内对堆内存分配添加标记,并检查 libc.so 中的内存操作的标记。如果错误的内存访问位于 libc.so 中(例如经过 delete() 的互斥量上的 pthread_mutex_unlock()),即使在未启用 HWASan 的二进制文件中,这也可能会捕获 bug。

如果整个平台是使用 HWASan 构建的,则无需更改任何 build 文件。

Flashstation

出于开发目的,您可以使用 Flashstation 将支持 HWASan 的 AOSP build 刷写到引导加载程序已解锁的 Pixel 设备上。 选择 _hwasan 目标,例如 aosp_flame_hwasan-userdebug。如需了解详情,请参阅面向应用开发者的 HWASan NDK 文档

更出色的堆栈轨迹

HWASan 使用基于帧指针的快速展开程序 (unwinder),根据程序中的每个内存分配和取消分配事件来记录堆栈跟踪信息。默认情况下,Android 在 AArch64 代码中启用帧指针,因此这在实际操作中非常有用。如果您需要通过托管代码展开,请在进程环境中设置 HWASAN_OPTIONS=fast_unwind_on_malloc=0。请注意,错误的内存访问堆栈跟踪会默认使用“慢速”展开程序;此设置仅影响分配和取消分配跟踪。此选项可能对 CPU 要求极高,具体取决于负载。

符号化

请参阅“了解 HWASan 报告”中的符号化

在应用中使用 HWASan

与 AddressSanitizer 类似,HWASan 无法检查 Java 代码,但可以检测 JNI 库中的错误。而与 ASan 不同的是,支持在非 HWASan 设备上运行 HWASan 应用。

在 HWASan 设备上,可以使用 HWASan 检查应用,方法是使用 Make 中的 SANITIZE_TARGET:=hwaddress 或编译器标志中的 -fsanitize=hwaddress 来构建代码。如需了解详情,请参阅应用开发者文档