了解 HWASan 报告

当 HWASan 工具检测到内存 bug 时,系统会通过 abort() 终止该进程,并将报告输出到 stderr 和 logcat。与 Android 上的所有原生代码崩溃问题一样,HWASan 错误也可以在 /data/tombstones 下找到。

与常规原生代码崩溃不同的是,HWASan 会在 Tombstone 顶部附近的“Abort message”字段中包含额外信息。请参阅下面基于堆的崩溃示例(有关堆栈 bug,请参阅下文堆栈专属部分的说明)。

示例报告

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/flame_hwasan/flame:Tiramisu/MASTER/7956676:userdebug/dev-keys'
Revision: 'DVT1.0'
ABI: 'arm64'
Timestamp: 2019-04-24 01:13:22+0000
pid: 11154, tid: 11154, name: sensors@1.0-ser  >>> /vendor/bin/hw/android.hardware.sensors@1.0-service <<<
signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
Abort message: '

[...]

[0x00433ae20040,0x00433ae20060) is a small unallocated heap chunk; size: 32 offset: 5








[ … regular crash dump follows …]

这与 AddressSanitizer 报告非常类似。 不同的是,几乎所有 HWASan bug 都是“标记不匹配”,即在访问内存时,指针标记与相应的内存标记不匹配。原因可能是以下几项之一:

  • 对堆栈或堆的访问出界
  • 对堆的释放后使用
  • 对堆栈的返回后使用

部分

以下是对 HWASan 报告各个部分的说明:

访问错误

包含与错误的内存访问有关的信息,其中包括:

  • 访问类型(“读取”与“写入”)
  • 访问大小(尝试访问的字节数)
  • 访问的线程数
  • 指针标记和内存标记(用于高级调试)

访问堆栈轨迹

错误的内存访问的堆栈轨迹。如需进行符号化处理,请参阅“符号化”部分

原因

造成访问错误的潜在原因。如果有多种候选原因,这些原因会按可能性降序排列,后跟有关潜在原因的详细信息。HWASan 可以诊断以下原因:

  • 释放后使用
  • 堆栈标记不匹配:可能是堆栈返回后使用/范围外使用或出界
  • 堆缓冲区溢出
  • 全局溢出

内存信息

描述 HWASan 对所访问内存的了解,可能会因 bug 类型而异。

bug 类型 原因 报告格式
标记不匹配 释放后使用

<address> is located N bytes inside of M-byte region [<start>, <end>)
  freed by thread T0 here:
堆缓冲区溢出 请注意,这也可能是下溢。

<address> is located N bytes to the right of M-byte region [<start>, <end>)
  allocated here:
堆栈标记不匹配 堆栈报告不会区分上溢/下溢和“返回后使用”bug。此外,如需查找作为错误来源的堆栈分配,需要执行离线符号化步骤。请参阅下面的了解堆栈报告部分。
无效释放 释放后使用 这是一个重复释放 bug。

<address> is located N bytes inside of M-byte region [<start>, <end>)
  freed by thread T0 here:
无法描述地址 从 HWASan 的可用缓冲区中逐出错误释放(之前未分配的内存释放)或分配内存后的重复释放。
0x… 是 HWASan 影子内存。 这显然是错误释放,因为应用尝试释放的是 HWASan 内部的内存。

取消分配堆栈轨迹

在取消分配内存情况下的堆栈轨迹。仅适用于“释放后使用”或“无效释放”bug。 如需进行符号化处理,请参阅“符号化”部分

分配堆栈轨迹

在分配内存情况下的堆栈轨迹。 如需进行符号化处理,请参阅“符号化”部分

高级调试信息

HWASan 报告还包含一些高级调试信息,其中依次包括:

  1. 进程中的线程列表
  2. 进程中的线程列表
  3. 错误内存附近的内存标记的值
  4. 内存访问点的寄存器转储

内存标记转储

标记内存转储可用于查找带有与指针标记相同的标记的附近内存分配。它们可能指向具有较大偏移的出界访问。一个标记对应 16 字节的内存;指针标记是地址的前 8 位。标记内存转储可以给出提示,例如,右侧是缓冲区溢出:

tags: ad/5c (ptr/mem)
[...]
Memory tags around the buggy address (one tag corresponds to 16 bytes):
  0x006f33ae1ff0: 0e  0e  0e  57  20  20  20  20  20  2e  5e  5e  5e  5e  5e  b5
=>0x006f33ae2000: f6  f6  f6  f6  f6  4c  ad  ad  ad  ad  ad  ad [5c] 5c  5c  5c
  0x006f33ae2010: 5c  04  2e  2e  2e  2e  2e  2f  66  66  66  66  66  80  6a  6a
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
  0x006f33ae1ff0: ab  52  eb  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=>0x006f33ae2000: ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  .. [..] ..  ..  ..
  0x006f33ae2010: ..  5c  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
(请注意,左侧运行的 6 × 16 = 96 字节的“ad”标记与指针标记相匹配)。

如果分配的大小不是 16 的倍数,其余大小将存储为内存标记,并且标记将存储为短粒度标记。在上面的示例中,在加粗的分配标记 ad 之后,我们对标记 5c 进行了 5 × 16 + 4 = 84 字节分配。

零内存标记(例如,tags: ad/00 (ptr/mem))通常表示“堆栈返回后使用”bug。

寄存器转储

HWASan 报告中的寄存器转储与执行无效内存访问的实际指令相对应。后跟常规 Android 信号处理程序 -ignore the second one 的另一个寄存器转储,它在 HWASan 调用 abort() 时采用,与 bug 无关。

符号化

如需获取堆栈轨迹中的函数名称和行号(以及获取“范围外使用”bug 的变量名称),需要执行离线符号化步骤。

首次设置:安装 llvm-symbolizer

如需进行符号化处理,您的系统必须安装 llvm-symbolizer 并且可通过 $PATH 访问。在 Debian 上,您可以使用 sudo apt install llvm 进行安装。

获取符号文件

为了进行符号化处理,我们需要包含符号的未剥离版二进制文件。这类文件的位置取决于 build 类型:

针对本地 build,符号文件可在 out/target/product/<product>/symbols/ 中找到。

针对 AOSP build(例如从 Flashstation 刷写),相应 build 可在 Android CI 上找到。该 build 的“工件”中将有一个“${PRODUCT}-symbols-${BUILDID}.zip”文件。

对于组织的内部 build,请参阅组织的文档,以获得与获取符号文件相关的帮助。

符号化处理

hwasan_symbolize –-symbols <DECOMPRESSED_DIR>/out/target/product/*/symbols < crash

了解堆栈报告

针对堆栈变量出现的 bug,HWASan 报告将包含如下详细信息:

Cause: stack tag-mismatch
Address 0x007d4d251e80 is located in stack of thread T64
Thread: T64 0x0074000b2000 stack: [0x007d4d14c000,0x007d4d255cb0) sz: 1088688 tls: [0x007d4d255fc0,0x007d4d259000)
Previously allocated frames:
  record_addr:0x7df7300c98 record:0x51ef007df3f70fb0  (/apex/com.android.art/lib64/libart.so+0x570fb0)
  record_addr:0x7df7300c90 record:0x5200007df3cdab74  (/apex/com.android.art/lib64/libart.so+0x2dab74)
  [...]
	

为了让堆栈 bug 易于理解,HWASan 会跟踪过去出现的堆栈帧。 目前,HWASan 不会在 bug 报告中将此类内容转换为人类可理解的内容,而是需要额外的符号化步骤

问题排查

“HWAddressSanitizer can not describe address in more detail.”

有时,HWASan 可能会因与过去的内存分配相关的信息而用尽空间。在这种情况下,报告将仅包含一条即时内存访问的堆栈轨迹,后跟备注:

  HWAddressSanitizer can not describe address in more detail.

在某些情况下,可通过多次运行测试来解决此问题。另一种方法是增加 HWASan 历史记录大小。此操作可以在 build/soong/cc/sanitize.go 中(查找 hwasanGlobalOptions)或您的进程环境中(可使用 adb shell echo $HWASAN_OPTIONS 查看当前设置)全局执行。

“nested bug in the same thread”

这意味着在生成 HWASan 崩溃报告时出现了 bug。这通常是因为 HWASan 运行时中存在 bug,请提交 bug 并尽可能提供有关如何重现该问题的说明。