HWAddress Sanitizer

从 NDK r21 和 Android 10(API 级别 29)开始,Android NDK 支持 HWAddress Sanitizer(也称为 HWASan)。

HWASan 是一款类似于 ASan 的内存错误检测工具。与传统的 ASan 相比,HWASan 具有如下特征:

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

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

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

此外,HWASan 还可以检测:

  • 返回之后的堆栈使用情况

设置

HWASan 应用需要 Android 的 HWASan build 才能运行。您可以将预构建的 HWASan 映像刷写到支持的 Pixel 手机上。在 ci.android.com 上可以找到这些 build,您可以在其中点击所需的确切 build(方格),以获取 Flash Build 链接。您需要知道您手机的代号

刷写设备 build

改为直接访问 flash.android.com 会使操作更简单,因为该流程会首先检测您的设备,且仅显示您可以使用的 build。下面的图片说明了此工具中的设置流程。

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

检测要刷写的设备 选择要连接的设备

您的设备连接后,您需要点击它来配置 build。在 Select a build ID 框中,选择 aosp-master-with-phones-throttled 分支以自动为您连接的设备选择正确的映像。

选择要刷写的设备 确认刷写选项并刷写设备

点击 Install 以刷写您的设备。

如需详细了解必要的设置,请参阅 Android 刷写工具文档。或者,您也可以查看 AOSP 文档,了解如何从源代码构建 HWASan 映像。

构建

要使用 HWAddress Sanitizer 构建应用的原生 (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)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=hwaddress)

运行

在 HWASan 构建的 Android 映像上照常运行应用。当检测到内存错误时,应用将因 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 寄存器值等。