了解 HWASan 報告

當 HWASan 工具偵測到記憶體錯誤時,流程會透過 abort() 終止,並將報告列印到 stderr 和 logcat。與 Android 上的所有本機崩潰一樣,可以在/data/tombstones下找到 HWASan 錯誤。

與常規的本機崩潰相比,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 報告各部分的解釋如下:

存取錯誤

包含有關錯誤記憶體存取的信息,包括:

  • 存取類型(“讀取”與“寫”)
  • 存取大小(嘗試存取多少位元組)
  • 訪問線程數
  • 指標和記憶體標籤(用於進階調試)

訪問堆疊追蹤

錯誤記憶體存取的堆疊追蹤。請參閱符號化部分進行符號化。

原因

訪問不良的潛在原因。如果有多個候選者,則按可能性降序排列。在有關潛在原因的詳細資訊之前。 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 內部的記憶體。

釋放堆疊追蹤

記憶體被釋放位置的堆疊追蹤。僅針對釋放後使用或無效釋放錯誤而出現。請參閱符號化部分進行符號化。

分配堆疊追蹤

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

進階除錯資訊

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 位元組的「ad」標記,與指標標記相符)。

如果分配的大小不是 16 的倍數,則剩餘大小將儲存為記憶體標記,並且該標記將儲存為短顆粒標記。在上面的範例中,在粗體分配標記廣告之後,我們分配了 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 建置(例如從Flashstation刷新),可以在Android CI上找到建置。在建置的「Artifacts」中,將會有一個 `${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 運行時中的錯誤造成的,請提交錯誤並提供有關如何重現該問題的說明(如果可能)。