瞭解 HWASan 報告

當 HWASan 工具偵測到記憶體錯誤時,該程序會以 abort() 終止,且 系統會輸出報表到 stderr 和 logcat。如同 Android 上的所有原生程式碼錯誤,HWASan 錯誤 在 /data/tombstones 下找到。

與一般原生程式碼錯誤相比,HWASan 會在「取消訊息」欄位中提供額外資訊 在墓碑頂端附近請參閱下方的堆積當機事件範例 (如需堆疊錯誤,請參閱下方注意事項,瞭解堆疊專屬章節)。

範例報表

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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 報告的各個部分:

存取錯誤

包含記憶體存取問題的相關資訊,包括:

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

存取堆疊追蹤

錯誤記憶體存取行為的堆疊追蹤。請參閱「符號化」一節, 符號化。

原因

發生存取問題的可能原因。如果有多個候選人 是 並按照可能性由高至低排列就在關於 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 可用緩衝區剔除分配的記憶體後。
0x... 是 HWAsan 陰影記憶體。 由於應用程式試圖 記憶體容量 HWASan 內部。

Deallocation 堆疊追蹤

記憶體釋放位置的堆疊追蹤。只用於釋放後使用 或無無效錯誤 如要進行符號化處理,請參閱符號化部分

配置堆疊追蹤

記憶體分配位置的堆疊追蹤。 如要進行符號化處理,請參閱符號化部分

進階偵錯 資訊

HWASan 報告也提供一些進階偵錯資訊,包括 (依序):

  1. 程序中的執行緒清單
  2. 程序中的執行緒清單
  3. 錯誤記憶體附近記憶體標記的值
  4. 存取記憶體時的暫存器轉儲

記憶體標記轉儲

可使用標記記憶體傾印,使用相同標記尋找鄰近的記憶體配置 作為 指標 標記之前。這些物件可能會指向外界存取,並具有較大的偏移量。1 個代碼 相當於 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 個位元組)。

如果分配的大小不是 16 的倍數,剩餘的大小會是 已儲存 作為 memory 標記,而標記則儲存為 short granule 標記,在上述範例中,緊接在加上粗體分配後才加上標示 我們 有 5 個 × 16 + 4 = 代碼 5c 的 84 位元配置。

零記憶體標記 (例如tags: ad/00 (ptr/mem)) 通常表示 傳回堆疊使用後發生的堆疊錯誤

註冊轉儲作業

HWASan 報表中的註冊傾印可對應至 無效 個人化記憶 資源存取權後面接著來自一般 Android 信號處理常式的另一個註冊傾印 - 忽略 第二種是,則會在 HWASan 呼叫 abort() 時採用,且與 錯誤。

符號化

取得堆疊追蹤中的函式名稱和行數 (並取得「使用後範圍」的變數名稱) 錯誤時,則需要執行離線符號化步驟。

首次設定:安裝 llvm-symbolizer

如要進行符號化處理,你的系統必須安裝 llvm-symbolizer,並透過 $PATH 存取。使用 Debian 時 即可透過 sudo apt install llvm 安裝。

取得符號檔案

進行符號化處理時,不得使用含有符號且未移除的二進位檔。顯示方式取決於 處理:

對於本機版本,符號檔案位於 out/target/product/<product>/symbols/

如果是 Android 開放原始碼計畫版本 (例如從 Flashstation 刷新), 您可以在 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 規定顯示使用「__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 執行階段,請回報錯誤,然後 盡可能提供重現問題的操作說明。