HWASan レポートについて

HWASan ツールがメモリのバグを検出すると、プロセスが abort() で終了し、stderr と logcat にレポートが出力されます。HWASan エラーは、Android のネイティブ コードでのクラッシュと同じ /data/tombstones に格納されます。

HWASan の追加情報は、通常のネイティブ コードでのクラッシュとは異なり、tombstone の上部にある「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 レポートの各セクションについて説明します。

アクセス エラー

不正なメモリアクセスに関する情報が含まれています。たとえば、以下に関する不正がレポートされます。

  • アクセスタイプ(「読み取り」と「書き込み」)
  • アクセスサイズ(アクセスが試行されたバイト数)
  • アクセスのスレッド数
  • ポインタとメモリタグ(高度なデバッグ用)

アクセスのスタック トレース

不正なメモリアクセスのスタック トレースです。シンボル化については、シンボル化のセクションをご覧ください。

原因

不正なアクセスの原因として考えられる候補です。候補が複数ある場合は、可能性が高い順に一覧表示されます。その後に、考えられる原因についての詳細情報が記載されます。HWASan では以下の原因を診断できます。

  • use-after-free(解放後の使用)
  • スタックの tag-mismatch(タグの不一致): スタックの use-after-return(返却後の使用)、use-after-scope(領域外の使用)、または境界外のアクセス
  • heap-buffer-overflow(ヒープのバッファ オーバーフロー)
  • global-overflow(グローバル オーバーフロー)

メモリ情報

アクセス対象のメモリについて HWASan が認識している情報です。バグの種類によって内容が異なる場合があります。

バグの種類 原因 レポート形式
tag-mismatch use-after-free(解放後の使用)

<address> is located N bytes inside of M-byte region [<start>, <end>)
  freed by thread T0 here:
heap-buffer-overflow(ヒープのバッファ オーバーフロー) アンダーフローの場合もあります。

<address> is located N bytes to the right of M-byte region [<start>, <end>)
  allocated here:
スタックの tag-mismatch スタック レポートでは、オーバーフロー / アンダーフローと use-after-return バグは区別されません。また、エラーの原因となったスタック割り当てを特定するには、オフラインでのシンボル化ステップが必要になります。下記のスタック レポートについてのセクションを参照してください。
invalid-free(無効な解放) use-after-free(解放後の使用) 二重解放バグです。

<address> is located N bytes inside of M-byte region [<start>, <end>)
  freed by thread T0 here:
アドレスを記述できない ワイルド解放(事前に割り当てられていなかったメモリの解放)、または割り当てられたメモリが HWASan の空きバッファから強制排除された後の二重解放です。
0x… が HWAsan シャドウメモリ。 アプリが HWASan の内部のメモリを解放しようとしていました。これは明らかにワイルド解放です。

割り当て解除のスタック トレース

メモリが割り当て解除された場合のスタック トレースです。use-after-free バグまたは invalid-free バグの場合にのみ表示されます。シンボル化については、シンボル化を参照してください。

割り当てのスタック トレース

メモリが割り当てられた場合のスタック トレースです。シンボル化については、シンボル化を参照してください。

高度なデバッグ情報

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  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
(この場合、ポインタタグに一致する位置より左の「ad」タグ 6 × 16 = 96 バイトは実行されます)

割り当てのサイズが 16 の倍数でない場合は、サイズの残りがメモリタグとして保存され、タグは短いグラニュール タグとして保存されます。上の例では、ad とタグ付けされた太字の割り当ての直後に、5 × 16 + 4 = 84 バイトのタグ 5c が割り当てられています。

ゼロメモリタグ(例: tags: ad/00 (ptr/mem))は、通常はスタック使用後のバグを示します。

レジスタダンプ

HWASan レポートのレジスタダンプは、不正なメモリアクセスが行われた実際の命令に対応しています。その後に、通常の Android シグナル ハンドラから別のレジスタダンプが続きます。2 番目のレジスタダンプは無視してください。これは HWASan が abort() を呼び出したときに記録されたもので、バグとは無関係です。

シンボル化

スタック トレース内の関数名と行番号(および use-after-scope バグの変数名)を取得するには、オフラインでのシンボル化ステップが必要になります。

初期設定: 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 では、過去に発生したスタック フレームを記録して、スタックのバグについて理解できるようにしています。ただし、現時点のバグレポートでは人間が理解できる形式には変換されておらず、追加のシンボル化ステップが必要になります。

トラブルシューティング

"HWAddressSanitizer can not describe address in more detail."(HWAddressSanitizer はアドレスをこれ以上詳しく記述できません。)

HWASan では、過去のメモリ割り当てに関する情報を格納する領域が不足することがあります。その場合、レポートには直近のメモリアクセスのスタック トレースが 1 つだけ含まれ、その後に次の注記が追加されます。

  HWAddressSanitizer can not describe address in more detail.

場合によっては、テストを複数回実行することでこの問題を解決できます。別の方法として、HWASan の履歴サイズを増やすこともできます。この設定は、build/soong/cc/sanitize.gohwasanGlobalOptions でグローバルに変更することもできますし、プロセス環境で変更することもできます(現在の設定は adb shell echo $HWASAN_OPTIONS で確認できます)。

"nested bug in the same thread"(同じスレッドにネストされたバグがあります)

これは、HWASan クラッシュ レポートの生成時にバグがあったことを意味します。通常は HWASan ランタイムのバグによるものです。バグを報告し、可能であれば問題を再現する方法をお知らせください。