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 バグが tag-mismatch(タグの不一致)エラーだという点で異なります。メモリアクセスにおいて、ポインタタグが対応するメモリタグと一致しない場合に発生するバグです。以下のいずれかである可能性があります。

  • スタックまたはヒープの境界外のアクセス
  • ヒープの use-after-free(解放後の使用)エラー
  • スタックの use-after-return(返却後の使用)エラー

セクション

HWASan レポートの各セクションについて説明します。

アクセスエラー

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

  • アクセスタイプ(READWRITE
  • アクセスサイズ(アクセスが試行されたバイト数)
  • アクセスのスレッド数
  • ポインタとメモリタグ(高度なデバッグ用)

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

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

原因

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

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

メモリ情報

アクセス対象のメモリについて 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:
スタックのタグの不一致 スタック レポートでは、オーバーフロー / アンダーフローのバグと use-after-return バグが区別されません。また、エラーの原因となったスタック割り当てを特定するには、オフラインでのシンボル化ステップが必要になります。スタック レポートについてをご覧ください。
無効な解放 解放後の使用 二重解放バグです。プロセスのシャットダウン時にこのような状況になった場合は、ODR 違反を示している可能性があります。
<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))は、stack-use-after-return バグを示します。

レジスタダンプ

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

シンボル化

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

初期設定: llvm-symbolizer をインストールする

シンボル化するには、システムに llvm-symbolizer をインストールし、$PATH からアクセスできるようにする必要があります。Debian では、sudo apt install llvm を使用してインストールできます。

シンボル ファイルを取得する

シンボル化には、シンボルが格納されたストリップされていないバイナリが必要です。このシンボル ファイルの場所は、ビルドの種類によって異なります。

  • ローカルビルドの場合、シンボル ファイルは out/target/product/<product>/symbols/ にあります。
  • AOSP ビルドの場合(たとえば Android Flash Tool からフラッシュする場合)、ビルドは 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 では、過去に発生したスタック フレームを記録して、スタックのバグについて理解できるようにしています。ただし、バグレポートでは人間が理解できる形式には変換されておらず、追加のシンボル化ステップが必要になります。

ODR 違反

HWASan によってレポートされる一部の use-after-free バグは、単一定義ルール(ODR)違反を示している場合もあります。ODR 違反は、同じプログラム内で同じ変数が複数回定義されているときに生じます。つまり、変数が複数回破壊されると use-after-free エラーが発生する可能性があります。

シンボル化後の ODR 違反は、__cxa_finalize での use-after-free エラーを示しています。不正アクセス スタックと freed here(ここで解放)スタックの両方に当てはまります。previously allocated here(直前にはここに割り当て)スタックには __dl__ZN6soinfo17call_constructorsEv が含まれていて、スタック上の上位変数を定義するプログラム内の場所をポイントします。

静的ライブラリが使用されている場合、ODR 違反になることがあります。C++ global を定義する静的ライブラリが、複数の共有ライブラリまたは実行可能ファイルにリンクされると、同じアドレス空間に同じシンボルの複数の定義が存在することになり、それが原因となって ODR 違反が生じます。

トラブルシューティング

このセクションでは、一部のエラーとその対処方法について説明します。

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 で確認できます)。

このエラーは、アクセスされたメモリがマッピングされていない場合、または HWASan を認識しないアロケータによって割り当てられた場合にも起こり得ます。その場合、クラッシュ ヘッダーに記載されている mem タグは通常 00 になります。すべての tombstone にアクセスできる場合は、メモリマップ ダンプを参照して、アドレスがどのマッピング(存在する場合)に属しているかを確認するとよいかもしれません。

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

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