HWAddressSanitizer

硬件辅助的 AddressSanitizer (HWASan) 是一款类似于 AddressSanitizer 的内存错误检测工具。与 ASan 相比,HWASan 使用的内存少得多,因而更适合用于清理整个系统。HWASan 仅适用于 Android 10 及更高版本,且只能用于 AArch64 硬件。您可以通过 ci.android.com,将预构建的 HWASan 映像刷写到支持的 Pixel 设备上(详细设置说明)。

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

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

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

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

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

实现详情和限制

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

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

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

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

HWASan 要求 Linux 内核接受系统调用参数中被标记的指针。Android 10 及更高分支中的 Pixel 2、Pixel 3、Pixel 3a 和 Pixel 4 设备预构建内核以及 AOSP 内核源代码库中的相应源分支对此提供支持。已知 Pixel 2、Pixel 3、Pixel 3a 和 Pixel 4 可与使用 HWASan 构建的整个系统配合使用。

android-4.14 及更高分支中的通用 Android 内核提供对非 Pixel 设备的支持,但 Android 4 专属分支(例如 android-4.14-q)不提供此支持。您还需要择优挑选 CL 1182220 中的新工具链,以及 CL 1164431 中的相应构建系统更改,以将 LLVM 工具链更新为 r370808。

使用 HWASan

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

    lunch aosp_walleye-userdebug # (or any other product)
    make SANITIZE_TARGET=hwaddress
    

与 ASan 不同,使用 HWASan 时无需构建两次,只需增量构建,没有特殊的刷写指令,不需要擦除,支持静态可执行文件,并且可以跳过除 libc 之外的任何库的清理。此外,也不要求在清理库后必须同时对链接到该库的任何可执行文件进行清理。

要跳过某个模块的清理,请使用 LOCAL_NOSANITIZE := hwaddresssanitize: { hwaddress: false }

更出色的堆栈跟踪

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

符号化

请参阅 ASan 文档中的符号化

在应用中使用 HWASan

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

在 HWASan 设备上,可以使用 HWASan 检查应用,方法是使用 Make 中的 SANITIZE_TARGET:=hwaddress 或编译器标记中的 -fsanitize=hwaddress 来构建代码。