HWAddressSanitizer (HWAsan)

# HWAddressSanitizer

注意：本文将介绍如何对 Android 平台组件使用 HWAddress Sanitizer。如需了解如何在 HWAddress Sanitizer 下运行使用 NDK 构建的 Android 应用，请参阅开发者文档。

*如需了解如何读取 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 内核接受系统调用参数中已加标记的指针。在下列上游补丁集中实现了对此项要求的支持：

* arm64 已标记地址 ABI
* arm64：对传递给内核的用户指针取消标记
* mm：避免在 brk()/mmap()/mremap() 中创建虚拟地址别名
* arm64：验证从内核线程调用的 access\_ok() 中的已标记地址

如果您要使用自定义工具链进行构建，请确保它包含直到 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

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

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