硬件地址消毒器

Android NDK 从 NDK r21 和 Android 10(API 级别 29)开始支持 硬件地址消毒器(也称为 HWASan)。HWASan 仅在 64 位 Arm 设备上可用。

HWASan 是一种与 ASan 类似的内存错误检测工具。与经典的 ASan 相比,HWASan 具有以下特点:

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

HWASan 检测与 ASan 相同的一组错误

  • 堆栈和堆缓冲区溢出或下溢
  • 释放后使用堆
  • 超出范围使用堆栈
  • 双重释放或无效释放

此外,HWASan 还检测以下错误:

  • 返回后使用堆栈

示例应用

一个 示例应用 演示了如何为 hwasan 配置 构建变体

构建

要使用 硬件地址消毒器 构建应用的原生(JNI)代码,请执行以下操作:

ndk-build

在您的 Application.mk 文件中

APP_STL := c++_shared # Or system, or none, but not c++_static.
APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=hwaddress

CMake

在您的模块的 build.gradle 文件中

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # Can also use system or none as ANDROID_STL, but not c++_static.
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }
}

对于 CMakeLists.txt 中的每个目标

target_compile_options(${TARGET} PUBLIC -fsanitize=hwaddress -fno-omit-frame-pointer)
target_link_options(${TARGET} PUBLIC -fsanitize=hwaddress)

对于 NDK 27 或更高版本,您也可以在 build.gradle 中使用以下内容,并且无需更改 CMakeLists.txt

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_SANITIZE=hwaddress"
            }
        }
    }
}

当使用 ANDROID_USE_LEGACY_TOOLCHAIN_FILE=false 时,此方法无效。

Android 14 或更高版本:添加 wrap.sh

如果您运行的是 Android 14 或更高版本,则可以使用 wrap.sh 脚本 在任何 Android 设备上运行您的可调试应用。如果您选择按照 设置说明 中的步骤操作,则可以跳过此步骤。

请按照说明 打包 wrap.sh 脚本,为 arm64-v8a 添加以下 wrap.sh 脚本。

#!/system/bin/sh
LD_HWASAN=1 exec "$@"

运行

如果您运行的是 Android 14 之前的版本,或者没有添加 wrap.sh 脚本,请在运行应用之前按照 设置说明 进行操作。

按照通常方式运行应用。当检测到内存错误时,应用会因 SIGABRT 崩溃并向 logcat 打印详细消息。该消息的副本可以在 /data/tombstones 下的文件中找到,内容如下所示

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

该消息后面可能会跟着其他调试信息,包括应用程序中所有存活线程的列表、附近内存分配的标签以及 CPU 寄存器值。

有关 HWASan 错误消息的更多信息,请参阅 了解 HWASan 报告

构建命令行可执行文件

您可以在 Android 14 及更高版本上构建和运行使用 HWASan 检测的可执行文件。您可以使用与 构建 中描述的相同配置(对于 ndk-build 或 CMake)来构建您的可执行文件。将可执行文件推送到运行 Android 14 或更高版本的设备上,并使用 shell 正常运行。

如果您使用的是 libc++,请确保您使用的是共享 STL,并将其推送到设备上,并在运行二进制文件时将 LD_LIBRARY_PATH 设置为包含它的目录。

如果您未使用 Gradle,请参阅 NDK 文档,了解如何使用 CMakendk-build 从命令行构建。

Android 13 或更低版本:设置

如果您的设备运行 Android 14 或更高版本,您可以跳过此步骤,并按照下面 构建 部分中 使用 wrap.sh 的说明 操作。您也可以选择遵循本节内容,并跳过下面使用 wrap.sh 的说明。

在 Android 14 之前,HWASan 应用程序需要使用 HWASan 构建的 Android 版本才能运行。您可以将预构建的 HWASan 镜像刷入受支持的 Pixel 设备。这些构建在 ci.android.com 上提供,您可以在其中点击您想要的特定构建的方块以获取 Flash Build 链接。这要求您知道 手机的代号

Flash a device build

或者,您可以直接转到 flash.android.com,因为该工具的工作流程检测您的设备开始,并且只会显示您可以使用的构建。以下图片说明了此工具中的设置流程。

在您的设备上启用开发者模式,并使用 USB 线将其连接到您的计算机。点击 添加新设备,从对话框中选择您的设备,然后点击 连接

Detect a device to flash Select the device to connect to

连接设备后,点击它以配置构建。在 选择构建 ID 框中,选择 aosp-master-with-phones-throttled 分支以自动为已连接的设备选择正确的镜像。

Select the device to flash Confirm flash options and flash the device

点击 安装 以刷入您的设备。

Android Flash Tool 文档 中提供了有关必要设置的更多详细信息。或者,您可以查看 AOSP 文档 以获取有关从源代码构建 HWASan 镜像的说明。