瞭解 HWASan 報告

HWASan 工具偵測到記憶體錯誤時,會以 abort() 終止程序,並將報告輸出至 stderr 和 Logcat。與 Android 上的所有原生當機情形一樣,HWASan 錯誤會顯示在 /data/tombstones 下方。

範例報表

與一般原生異常終止情形相比,HWASan 會在位於墓碑頂端附近的「Abort message」欄位中提供額外資訊。以下是堆積式異常終止的範例。如要瞭解堆疊錯誤,請參閱堆疊專屬章節的附註

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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 錯誤都是標記不相符的錯誤,也就是指標標記與對應的記憶體標記不相符的記憶體存取情形。這可以是下列任一項目:

  • 在堆疊或堆積上超出邊界存取
  • 堆積上的釋放後使用錯誤
  • 堆疊上的回傳後使用錯誤

區段

以下說明 HWASan 報表的各個部分。

存取錯誤

包含不正確記憶體存取的相關資訊,包括:

  • 存取類型 (READWRITE)
  • 存取大小 (嘗試存取的位元組數量)
  • 存取的執行緒編號
  • 指標和記憶體標記 (用於進階偵錯)

存取堆疊追蹤記錄

錯誤記憶體存取的堆疊追蹤。請參閱「符號化」一文,瞭解如何符號化。

原因

導致存取權異常的可能原因。如果有多個候選項,系統會依可能性高低列出。此欄位會先顯示潛在原因的詳細資訊。HWASan 可診斷以下原因:

  • 釋放後使用
  • 堆疊標記不相符,可能為回傳後使用堆疊、範圍後使用堆疊或超出邊界
  • 堆積緩衝區溢位
  • 全域溢位

記憶體資訊

說明 HWASan 對存取記憶體的瞭解程度,這可能因錯誤類型而異:

錯誤類型 原因 報表格式
標記不符 釋放後使用 請使用以下報表格式:
<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:
堆疊標記不符 堆疊報表不會區分溢位或反向溢位和使用後回傳錯誤。此外,如要找出錯誤來源的堆疊配置,您必須執行離線符號化步驟。請參閱「瞭解堆疊報表」。
無效的免費 釋放後使用 重複釋放錯誤。如果在程序關閉時發生這種情況,可能表示發生ODR 違規
<address> is located N bytes inside of M-byte region [<start>, <end>)
freed by thread T0 here:
無法描述地址 要不是隨機釋放 (釋出先前未分配的記憶體),就是在已分配的記憶體從 HWASan 的 free 緩衝區中淘汰後,釋放兩次。
0x... 是 HWAsan 陰影記憶體 應用程式嘗試釋放 HWASan 內部的記憶體,因此發生記憶體釋放異常。

解除配置堆疊追蹤

記憶體釋放位置的堆疊追蹤。僅針對使用釋放後記憶體或無效記憶體錯誤顯示。請參閱「符號化」一文,瞭解如何進行符號化。

配置堆疊追蹤

記憶體分配位置的堆疊追蹤。請參閱「符號化」一文,瞭解如何進行符號化。

進階偵錯資訊

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  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..

請注意,左側的 ad 標記有 6 × 16 = 96 個位元組,與指標標記相符。

如果配置的大小不是 16 的倍數,大小的餘數會儲存為 記憶體標記,而標記會儲存為 短精細標記。在前述範例中,在標記為 ad 的粗體分配值之後,我們有 5 × 16 + 4 = 84 個位元組的標記 5c 分配值。

零記憶體標記 (例如 tags: ad/00 (ptr/mem)) 表示「使用已釋放的堆疊」錯誤。

註冊傾印

HWASan 報告中的登錄檔轉儲作業會對應至執行無效記憶體存取作業的指令。此傾印作業之後,會由一般 Android 信號處理常式程式執行另一個註冊傾印作業。忽略第二個傾印,因為該傾印是在 HWASan 呼叫 abort() 時擷取,與錯誤無關。

符號化

如要取得堆疊追蹤中的函式名稱和行號 (以及取得用於作用範圍後使用錯誤的變數名稱),就必須進行離線符號化步驟。

首次設定:安裝 llvm-symbolizer

如要進行符號化,系統必須安裝 llvm-symbolizer,並可從 $PATH 存取。在 Debian 上,您可以使用 sudo apt install llvm 安裝。

取得符號檔案

符號化功能需要未經去除的二進位檔,其中包含符號。位置取決於版本類型:

  • 對於本機版本,符號檔案位於 out/target/product/<product>/symbols/ 中。
  • 對於 AOSP 版本 (例如透過 Android Flash Tool 刷新),這些版本會在 Android CI 上執行。在建構的「構件」中,有一個 ${PRODUCT}-symbols-${BUILDID}.zip 檔案。
  • 如果是貴機構的內部版本,請參閱貴機構的文件,瞭解如何取得符號檔案。

符號化

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

瞭解堆疊報表

如果是堆疊變數發生的錯誤,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)
  [...]

為協助您瞭解堆疊錯誤,HWASan 會追蹤過去的堆疊框架。HWASan 不會將這些內容轉換為錯誤報告中人類可理解的內容,因此需要額外的符號化步驟

ODR 違規

HWASan 回報的部分使用後釋放錯誤,可能表示違反「單一定義規則」(ODR)。如果同一個程式中定義了相同的變數多次,就會發生 ODR 違規。這也表示變數會多次解構,可能會導致使用後釋放錯誤。

符號化後,ODR 違規事項會在無效存取堆疊和 freed here 堆疊上,顯示 __cxa_finalize 的使用後釋放錯誤。先前在此處分配的堆疊包含 __dl__ZN6soinfo17call_constructorsEv,且應指向程式中定義堆疊上方變數的位置。

如果使用靜態程式庫,可能會違反 ODR。如果定義 C++ 全域的靜態程式庫連結至多個共用程式庫或可執行檔,同一個位址空間可能會存在多個相同符號的定義,進而導致 ODR 錯誤。

疑難排解

本節將說明一些錯誤及其解決方法。

HWAddressSanitizer 無法進一步說明地址

有時 HWASan 可能會用盡空間,無法儲存過去記憶體配置的資訊。在這種情況下,報告只會包含一個立即記憶體存取的堆疊追蹤,並附上以下註解:

HWAddressSanitizer can not describe address in more detail.

在某些情況下,您可以多次執行測試來解決這個問題。另一個選項是增加 HWASan 記錄大小。您可以在 build/soong/cc/sanitize.go 中全域執行這項操作 (請查看 hwasanGlobalOptions),或在程序環境中執行 (請嘗試 adb shell echo $HWASAN_OPTIONS 查看目前的設定)。

如果未對存取的記憶體進行對應,或由非 HWASan 感知的配置器配置記憶體,也可能發生此錯誤。在這種情況下,當機標頭中列出的 mem 標記通常是 00。如果您可以存取完整的墓碑,建議您查看記憶體對應資料轉儲,找出地址屬於哪個對應 (如果有的話)。

同一個執行緒中的巢狀錯誤

這表示產生 HWASan 當機報告時發生錯誤。這通常是因為 HWASan 執行階段中的錯誤。回報錯誤,並提供重現問題的操作說明 (如有)。