请参阅了解 HWASan 报告,了解如何解读 HWASan 崩溃报告!
硬件辅助 AddressSanitizer (HWASan) 是一款内存错误检测工具,类似于 AddressSanitizer。与 ASan 相比,HWASan 使用的 RAM 少得多,因此适合全系统清理。HWASan 仅在 Android 10 及更高版本上提供,且仅适用于 AArch64 硬件。
虽然 HWASan 主要用于 C/C++ 代码,但它也可以帮助调试导致 C/C++(用于实现 Java 接口)崩溃的 Java 代码。它很有用,因为它会在内存错误发生时捕获它们,从而直接指向负责的代码。
您可以从 ci.android.com 将预构建的 HWASan 映像刷写到受支持的 Pixel 设备(详细设置说明)。
与经典 ASan 相比,HWASan 具有:
- 类似的 CPU 开销(约 2 倍)
- 类似的代码大小开销(40–50%)
- 更小的 RAM 开销(10%–35%)
HWASan 检测到的错误集与 ASan 相同:
- 堆栈和堆缓冲区溢出/下溢
- 堆释放后使用
- 堆栈超出范围使用
- 双重释放/野指针释放
此外,HWASan 还会检测堆栈返回后使用。
HWASan(与 ASan 相同)与 UBSan 兼容,两者可以同时在目标上启用。
实现详情和限制
HWASan 基于内存标记方法,其中小随机标记值与指针和内存地址范围相关联。为了使内存访问有效,指针标记和内存标记必须匹配。HWASan 依赖于 ARMv8 功能“最高字节忽略 (TBI)”,也称为虚拟地址标记,以将指针标记存储在地址的最高位中。
您可以在 Clang 文档网站上阅读有关 HWASan 设计的更多信息。
根据设计,HWASan 没有 ASan 用于检测溢出的有限大小的红区,也没有 ASan 用于检测释放后使用的有限容量的隔离区。因此,无论溢出有多大或内存被释放多久,HWASan 都可以检测到错误。这使 HWASan 比 ASan 具有很大的优势。
但是,HWASan 的可能标记值数量有限(256 个),这意味着在程序的一次执行期间,有 0.4% 的概率会遗漏任何错误。
要求
通用 Android 内核的最新版本 (4.14+) 开箱即可支持 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。
- 增量构建开箱即可用。
- 无需刷写 userdata。
某些 AddressSanitizer 限制也已消失:
- 支持静态可执行文件。
- 可以跳过对 libc 以外的任何目标的清理。与 ASan 不同,没有这样的要求:如果库已清理,则链接它的任何可执行文件也必须清理。
可以在相同(或更高)构建编号下自由切换 HWASan 和常规映像。无需擦除设备。
要跳过对模块的清理,请使用 LOCAL_NOSANITIZE := hwaddress
(Android.mk) 或 sanitize: { hwaddress: false }
(Android.bp)。
清理单个目标
只要 libc.so
也经过清理,就可以在常规(未清理)构建中按目标启用 HWASan。在 bionic/libc/Android.bp 中的 "libc_defaults"
中的 sanitize 块中添加 hwaddress: true
。然后在您正在处理的目标中执行相同的操作。
请注意,清理 libc 会在系统范围内启用堆内存分配的标记,以及检查 libc.so
内部的内存操作的标记。即使在未启用 HWASan 的二进制文件中,如果错误的内存访问在 libc.so
中(例如,对 delete()
的互斥锁执行 pthread_mutex_unlock()
),也可能会捕获到错误。
如果使用 HWASan 构建整个平台,则无需更改任何构建文件。
Flashstation
出于开发目的,您可以使用 Flashstation 将启用 HWASan 的 AOSP 版本刷写到具有已解锁引导加载程序的 Pixel 设备上。选择 _hwasan 目标,例如 aosp_flame_hwasan-userdebug。有关更多详细信息,请参阅面向应用开发者的 HWASan NDK 文档。
更好的堆栈轨迹
HWASan 使用基于快速帧指针的展开器来记录程序中每个内存分配和释放事件的堆栈轨迹。Android 默认在 AArch64 代码中启用帧指针,因此这在实践中效果很好。如果您需要展开托管代码,请在进程环境中设置 HWASAN_OPTIONS=fast_unwind_on_malloc=0
。请注意,错误的内存访问堆栈轨迹默认使用“慢速”展开器;此设置仅影响分配和释放轨迹。此选项可能会非常占用 CPU 资源,具体取决于负载。
符号化
请参阅“了解 HWASan 报告”中的符号化。
应用中的 HWASan
与 AddressSanitizer 类似,HWASan 无法查看 Java 代码,但它可以检测 JNI 库中的错误。在 Android 14 之前,不支持在非 HWASan 设备上运行 HWASan 应用。
在 HWASan 设备上,可以通过在 Make 中使用 SANITIZE_TARGET:=hwaddress
或在编译器标志中使用 -fsanitize=hwaddress
构建应用的代码,从而使用 HWASan 检查应用。在非 HWASan 设备(运行 Android 14 或更高版本)上,必须添加设置 LD_HWASAN=1
的 wrap.sh 文件。有关更多详细信息,请参阅应用开发者文档。