从 NDK r21 和 Android 10(API 级别 29)开始,Android NDK 支持 HWAddress Sanitizer(也称为 HWASan)。HWASan 仅适用于 64 位 Arm 设备。
HWASan 是一款类似于 ASan 的内存错误检测工具。与传统的 ASan 相比,HWASan 具有如下特征:
- 类似的 CPU 开销(约为 2 倍)
- 类似的代码大小开销 (40 - 50%)
- 更小的 RAM 开销 (10% - 35%)
HWASan 能检测到 ASan 所能检测到的同一系列错误:
- 堆栈和堆缓冲区上溢或下溢
- 释放之后的堆使用情况
- 超出范围的堆栈使用情况
- 重复释放或错误释放
此外,HWASan 还可以检测:
- 返回之后的堆栈使用情况
示例应用
示例应用展示了如何为 hwasan 配置 build 变体。
构建
若要使用 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)
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 文档,了解如何使用 CMake 和 ndk-build 从命令行构建。
Android 13 或更低版本:设置
如果您的设备搭载的是 Android 14 或更高版本,您可以跳过此步骤,然后按照构建部分中的使用 wrap.sh 的说明进行操作。您也可以选择按照本部分操作,并跳过使用 wrap.sh 的说明。
在 Android 14 之前,HWASan 应用需要 Android 的 HWASan build 才能运行。您可以将预构建的 HWASan 映像刷写到支持的 Pixel 设备上。在 ci.android.com 上可以找到这些 build,您可以在此页面上点击所需的确切 build 的方格来获取 Flash Build 链接。您需要知道您手机的代号。
改为直接访问 flash.android.com 会使操作更简单,因为该流程会首先检测您的设备,且仅显示您可以使用的 build。下面的图片说明了此工具中的设置流程。
在设备上启用开发者模式,然后使用 USB 线将其连接到计算机。点击 Add new device,从对话框中选择您的设备,然后点击 Connect。
您的设备连接后,点击它来配置 build。在 Select a build ID 框中,选择 aosp-master-with-phones-throttled
分支以自动为您连接的设备选择正确的映像。
点击 Install 以刷写您的设备。
如需详细了解必要的设置,请参阅 Android 刷写工具文档。或者,您也可以查看 AOSP 文档,了解如何从源代码构建 HWASan 映像。