Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

ネイティブ コードでのクラッシュを診断する

以下のセクションでは、ネイティブ コードでのクラッシュ(以下「ネイティブ クラッシュ」)の一般的なタイプを示し、サンプルであるクラッシュ ダンプの分析と tombstone の調査について説明します。クラッシュの各タイプの説明では、debuggerd のサンプル出力の中で、特定の種類のクラッシュを識別するうえで役立つ重要な痕跡を強調表示しています。

中止(abort)

中止は、意図的に行われる点で、興味深い手法です。中止を行う方法はさまざまです。たとえば、abort(3) を呼び出す、assert(3) を失敗させる、Android 固有の「fatal」なロギングタイプの 1 つを使用するなどです。しかし、どの方法も abort を呼び出します。abort の呼び出しは、呼び出し元スレッドに SIGABRT シグナルを送ります。したがって、debuggerd 出力でこのケースを識別するには、libc.so に「abort」が表示されているフレームと SIGABRT を探します。

「abort message」と明示された行があるはずです。また、logcat 出力を調べて、このスレッドが意図的に中止される前に何を記録したかを確認する必要があります。assert(3) または高レベルの fatal ロギング機能と異なり、abort(3) はメッセージを受け入れないからです。

Android の最近のバージョンは tgkill(2) システムコールをインライン化するので、スタックが大変読みやすくなっており、一番上に abort(3) 呼び出しが表示されます。

    pid: 4637, tid: 4637, name: crasher  >>> crasher <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'some_file.c:123: some_function: assertion "false" failed'
        r0  00000000  r1  0000121d  r2  00000006  r3  00000008
        r4  0000121d  r5  0000121d  r6  ffb44a1c  r7  0000010c
        r8  00000000  r9  00000000  r10 00000000  r11 00000000
        ip  ffb44c20  sp  ffb44a08  lr  eace2b0b  pc  eace2b16
    backtrace:
        #00 pc 0001cb16  /system/lib/libc.so (abort+57)
        #01 pc 0001cd8f  /system/lib/libc.so (__assert2+22)
        #02 pc 00001531  /system/bin/crasher (do_action+764)
        #03 pc 00002301  /system/bin/crasher (main+68)
        #04 pc 0008a809  /system/lib/libc.so (__libc_init+48)
        #05 pc 00001097  /system/bin/crasher (_start_main+38)
    

古いバージョンの Android では、元の abort 呼び出し(この例ではフレーム 4)と実際のシグナル送信(この例ではフレーム 0)との間で複雑なパスを経由していました。32 ビット ARM では特に複雑で、__libc_android_abort(この例ではフレーム 3)が他のプラットフォームの raise/pthread_kill/tgkill のシーケンスに追加されていました。

    pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'some_file.c:123: some_function: assertion "false" failed'
        r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
        r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
        r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
        ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
    backtrace:
        #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
        #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
        #02 pc 0001bb87  /system/lib/libc.so (raise+10)
        #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
        #04 pc 000168e8  /system/lib/libc.so (abort+4)
        #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
        #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
        #07 pc 00000f21  /system/xbin/crasher
        #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
        #09 pc 00000abc  /system/xbin/crasher
    

このタイプのクラッシュのインスタンスを再現するには、crasher abort を使用します。

単なる null ポインタ逆参照(デリファレンス)

これは古典的なネイティブ クラッシュであり、次に示すクラッシュ タイプの特殊ケースにすぎませんが、それとは別に取り上げる価値があります。通常は簡単に見つけられるからです。

次の例では、文字列関数は指定されたポインタで動作するだけなので、クラッシュした関数が libc.so 内に存在するにもかかわらず、strlen(3) が null ポインタで呼び出されたと推論できます。つまり、このクラッシュの原因は呼び出し元コードの作成者に帰せられます。この例では、不適切な呼び出し元はフレーム #01 です。

    pid: 25326, tid: 25326, name: crasher  >>> crasher <<<
    signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
        r0 00000000  r1 00000000  r2 00004c00  r3 00000000
        r4 ab088071  r5 fff92b34  r6 00000002  r7 fff92b40
        r8 00000000  r9 00000000  sl 00000000  fp fff92b2c
        ip ab08cfc4  sp fff92a08  lr ab087a93  pc efb78988  cpsr 600d0030

    backtrace:
        #00 pc 00019988  /system/lib/libc.so (strlen+71)
        #01 pc 00001a8f  /system/xbin/crasher (strlen_null+22)
        #02 pc 000017cd  /system/xbin/crasher (do_action+948)
        #03 pc 000020d5  /system/xbin/crasher (main+100)
        #04 pc 000177a1  /system/lib/libc.so (__libc_init+48)
        #05 pc 000010e4  /system/xbin/crasher (_start+96)
    

このタイプのクラッシュのインスタンスを再現するには、crasher strlen-NULL を使用します。

低アドレスの null ポインタ逆参照

多くの場合、フォールト アドレスは 0 ではなく、他の低い番号です。特に 2 桁または 3 桁のアドレスはよくありますが、6 桁のアドレスはほぼ確実に null ポインタ逆参照ではありません。それには 1 MiB のオフセットが必要です。通常これは、null ポインタを有効な構造体であるかのように見なして逆参照するコードが存在する場合に発生します。よくある関数は、fprintf(3)(または FILE* を受け取る他の任意の関数)と readdir(3) です。なぜなら、fopen(3) または opendir(3) の呼び出しが実際は最初に成功したことをコードが確認できない場合がしばしばあるからです。

readdir の例を次に示します。

    pid: 25405, tid: 25405, name: crasher  >>> crasher <<<
    signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
        r0 0000000c  r1 00000000  r2 00000000  r3 3d5f0000
        r4 00000000  r5 0000000c  r6 00000002  r7 ff8618f0
        r8 00000000  r9 00000000  sl 00000000  fp ff8618dc
        ip edaa6834  sp ff8617a8  lr eda34a1f  pc eda618f6  cpsr 600d0030

    backtrace:
        #00 pc 000478f6  /system/lib/libc.so (pthread_mutex_lock+1)
        #01 pc 0001aa1b  /system/lib/libc.so (readdir+10)
        #02 pc 00001b35  /system/xbin/crasher (readdir_null+20)
        #03 pc 00001815  /system/xbin/crasher (do_action+976)
        #04 pc 000021e5  /system/xbin/crasher (main+100)
        #05 pc 000177a1  /system/lib/libc.so (__libc_init+48)
        #06 pc 00001110  /system/xbin/crasher (_start+96)
    

この例では、クラッシュの直接の原因は、pthread_mutex_lock(3) がアドレス 0xc(フレーム 0)にアクセスしようとしたことです。しかし、pthread_mutex_lock が最初に行ったのは、与えられた pthread_mutex_t*state 要素の逆参照です。ソースを見ると、その要素が構造体のオフセット 0 にあることがわかります。これは、pthread_mutex_lock に無効なポインタ 0xc が渡されたことを示しています。フレーム 1 から、readdir(与えられた DIR* から mutex_ フィールドを抽出します)によってそのポインタが渡されたことがわかります。その構造体を見ると、mutex_struct DIR へのオフセット sizeof(int) + sizeof(size_t) + sizeof(dirent*) にあることがわかります。これは、32 ビットデバイスでは 4 + 4 + 4 = 12 = 0xc です。呼び出し元から readdir に null ポインタが渡されているバグが見つかりました。この時点で、スタックをスタックツールに貼り付けて、logcat のどこで問題が発生したかを解明できます。

      struct DIR {
        int fd_;
        size_t available_bytes_;
        dirent* next_;
        pthread_mutex_t mutex_;
        dirent buff_[15];
        long current_pos_;
      };
    

実際には、ほとんどの場合この分析はスキップできます。一般的に、フォールト アドレスが十分に低い番号であれば、スタック内の libc.so フレームをすべてスキップして、呼び出し元コードを直接見つけられます。ただし、必ずしもそうとは限らず、上記のような分析が必要になるケースもあります。

このタイプのクラッシュのインスタンスを再現するには、crasher fprintf-NULL または crasher readdir-NULL を使用します。

FORTIFY エラー

FORTIFY エラーは、C ライブラリがセキュリティ脆弱性につながる可能性がある問題を検出した場合に発生する、中止の特殊なケースです。多くの C ライブラリ関数には FORTIFY が実装されています。それらは、バッファの実際の大きさを知らせる追加の引数を取り、実行しようとしているオペレーションが実際に適切かどうかを実行時にチェックします。実際には 10 バイトしかないバッファに対して read(fd, buf, 32) を実行しようとするコードの例を次に示します。

    pid: 25579, tid: 25579, name: crasher  >>> crasher <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer'
        r0 00000000  r1 000063eb  r2 00000006  r3 00000008
        r4 ff96f350  r5 000063eb  r6 000063eb  r7 0000010c
        r8 00000000  r9 00000000  sl 00000000  fp ff96f49c
        ip 00000000  sp ff96f340  lr ee83ece3  pc ee86ef0c  cpsr 000d0010

    backtrace:
        #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
        #01 pc 00019cdf  /system/lib/libc.so (abort+50)
        #02 pc 0001e197  /system/lib/libc.so (__fortify_fatal+30)
        #03 pc 0001baf9  /system/lib/libc.so (__read_chk+48)
        #04 pc 0000165b  /system/xbin/crasher (do_action+534)
        #05 pc 000021e5  /system/xbin/crasher (main+100)
        #06 pc 000177a1  /system/lib/libc.so (__libc_init+48)
        #07 pc 00001110  /system/xbin/crasher (_start+96)
    

このタイプのクラッシュのインスタンスを再現するには、crasher fortify を使用します。

-fstack-protector によって検出されるスタックの破損

コンパイラの -fstack-protector オプションは、バッファ オーバーランを防ぐために、オンスタック バッファを使用する関数にチェックを挿入します。このオプションは、プラットフォーム コードではデフォルトで有効になっていますが、アプリでは有効になっていません。このオプションを有効にすると、コンパイラによって関数プロローグに命令が追加され、スタックにある最後のローカルの直後にランダム値が書き込まれます。また、関数エピローグに命令が追加され、その値が再び読み取られて変更されていないかどうかがチェックされます。値が変更されていれば、バッファ オーバーランによって上書きされたことがわかるので、エピローグは __stack_chk_fail を呼び出してメッセージをログに記録し、実行を中止します。

    pid: 26717, tid: 26717, name: crasher  >>> crasher <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'stack corruption detected'
        r0 00000000  r1 0000685d  r2 00000006  r3 00000008
        r4 ffd516d8  r5 0000685d  r6 0000685d  r7 0000010c
        r8 00000000  r9 00000000  sl 00000000  fp ffd518bc
        ip 00000000  sp ffd516c8  lr ee63ece3  pc ee66ef0c  cpsr 000e0010

    backtrace:
        #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
        #01 pc 00019cdf  /system/lib/libc.so (abort+50)
        #02 pc 0001e07d  /system/lib/libc.so (__libc_fatal+24)
        #03 pc 0004863f  /system/lib/libc.so (__stack_chk_fail+6)
        #04 pc 000013ed  /system/xbin/crasher (smash_stack+76)
        #05 pc 00001591  /system/xbin/crasher (do_action+280)
        #06 pc 00002219  /system/xbin/crasher (main+100)
        #07 pc 000177a1  /system/lib/libc.so (__libc_init+48)
        #08 pc 00001144  /system/xbin/crasher (_start+96)
    

この種類の中止は、バックトレースに __stack_chk_fail が存在することと、特定の abort メッセージによって、他の種類の中止から区別できます。

このタイプのクラッシュのインスタンスを再現するには、crasher smash-stack を使用します。

許可されていないシステムコールからの Seccomp SIGSYS

seccomp システム(特に seccomp-bpf)は、システムコールへのアクセスを制限します。プラットフォーム デベロッパー向けの seccomp の詳細情報については、ブログ記事 Android O の Seccomp フィルタをご覧ください。制限されたシステムコールを呼び出すスレッドは、コード SYS_SECCOMP を含む SIGSYS シグナルを受信します。システムコール番号は、アーキテクチャとともに、原因を示す行に表示されます。システムコール番号は、アーキテクチャによって異なることにご注意ください。たとえば、readlinkat(2) システムコールの番号は、x86 では 305、x86-64 では 267 です。コール番号は、arm と arm64 でも異なります。システムコール番号はアーキテクチャによって異なるので、ヘッダーでシステムコール番号を探すよりも、スタック トレースを使用して許可されていないシステムコールを調べる方が簡単です。

    pid: 11046, tid: 11046, name: crasher  >>> crasher <<<
    signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr --------
    Cause: seccomp prevented call to disallowed arm system call 99999
        r0 cfda0444  r1 00000014  r2 40000000  r3 00000000
        r4 00000000  r5 00000000  r6 00000000  r7 0001869f
        r8 00000000  r9 00000000  sl 00000000  fp fffefa58
        ip fffef898  sp fffef888  lr 00401997  pc f74f3658  cpsr 600f0010

    backtrace:
        #00 pc 00019658  /system/lib/libc.so (syscall+32)
        #01 pc 00001993  /system/bin/crasher (do_action+1474)
        #02 pc 00002699  /system/bin/crasher (main+68)
        #03 pc 0007c60d  /system/lib/libc.so (__libc_init+48)
        #04 pc 000011b0  /system/bin/crasher (_start_main+72)
    

許可されていないシステムコールは、シグナルの行に SYS_SECCOMP が存在することと、原因の行の説明によって、他のクラッシュから区別できます。

このタイプのクラッシュのインスタンスを再現するには、crasher seccomp を使用します。

実行専用メモリ違反

Android 10 以上の arm64 では、バイナリとライブラリの実行可能セグメントは、コード再利用攻撃に対するセキュリティ強化手法として、実行専用(読み取り不可)メモリにマッピングされます。読み取りプリミティブを使用すると、前もってメモリ レイアウトを知らなくても、実行可能メモリで既知の関数を検索するかガジェット チェーンを構築することにより、アドレス空間レイアウト ランダム化(ASLR)をバイパスできます。実行可能コードを読み取り不可としてマークすると、読み取りプリミティブは実行可能メモリにアクセスできず、これにより特定の種類の不正利用を完全に遮断できます。 したがって、ASLR の有効性が向上するだけでなく、制御フローの整合性(CFI)も向上します。読み取りプリミティブを使用する攻撃者に有効な宛先が開示されなくなるからです。

コードを読み取り不可にすると、実行専用としてマークされたメモリ セグメントに対する意図的な読み取りと意図的でない読み取りは、コード SEGV_ACCERR を含む SIGSEGV をスローします。これは、バグ、脆弱性、コードが混在したデータ(リテラルプールなど)、意図的なメモリ イントロスペクションの結果として発生します。

コンパイラはコードとデータが混在していないと想定しますが、手書きのアセンブリが元で問題が発生する可能性があります。多くの場合、この問題は定数を .data セクションに移動するだけで解決できます。実行可能コード セクションでコード イントロスペクションが必要不可欠である場合は、最初に mprotect(2) を呼び出してコードを読み取り可能としてマークし、オペレーションの完了後に再度読み取り不可としてマークする必要があります。

    pid: 2938, tid: 2940, name: crasher64  >>> crasher64 <<<
    signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x5f2ced24a8
    Cause: execute-only (no-read) memory access error; likely due to data in .text.
        x0  0000000000000000  x1  0000005f2cecf21f  x2  0000000000000078  x3  0000000000000053
        x4  0000000000000074  x5  8000000000000000  x6  ff71646772607162  x7  00000020dcf0d16c
        x8  0000005f2ced24a8  x9  000000781251c55e  x10 0000000000000000  x11 0000000000000000
        x12 0000000000000014  x13 ffffffffffffffff  x14 0000000000000002  x15 ffffffffffffffff
        x16 0000005f2ced52f0  x17 00000078125c0ed8  x18 0000007810e8e000  x19 00000078119fbd50
        x20 00000078125d6020  x21 00000078119fbd50  x22 00000b7a00000b7a  x23 00000078119fbdd8
        x24 00000078119fbd50  x25 00000078119fbd50  x26 00000078119fc018  x27 00000078128ea020
        x28 00000078119fc020  x29 00000078119fbcb0
        sp  00000078119fba40  lr  0000005f2ced1b94  pc  0000005f2ced1ba4

    backtrace:
          #00 pc 0000000000003ba4  /system/bin/crasher64 (do_action+2348)
          #01 pc 0000000000003234  /system/bin/crasher64 (thread_callback+44)
          #02 pc 00000000000e2044  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
          #03 pc 0000000000083de0  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
    

実行専用メモリ違反は、原因の行によって他のクラッシュから区別できます。

このタイプのクラッシュのインスタンスを再現するには、crasher xom を使用します。

fdsan によって検出されるエラー

Android のファイル記述子サニタイザー(fdsan)は、use-after-close や double-close などのファイル記述子によくある誤りをキャッチするのに役立ちます。この種類のエラーのデバッグ(と防止)については、fdsan のドキュメントをご覧ください。

    pid: 32315, tid: 32315, name: crasher64  >>> crasher64 <<<
    signal 35 (), code -1 (SI_QUEUE), fault addr --------
    Abort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by FILE* 0x7d8e413018'
        x0  0000000000000000  x1  0000000000007e3b  x2  0000000000000023  x3  0000007fe7300bb0
        x4  3033313465386437  x5  3033313465386437  x6  3033313465386437  x7  3831303331346538
        x8  00000000000000f0  x9  0000000000000000  x10 0000000000000059  x11 0000000000000034
        x12 0000007d8ebc3a49  x13 0000007fe730077a  x14 0000007fe730077a  x15 0000000000000000
        x16 0000007d8ec9a7b8  x17 0000007d8ec779f0  x18 0000007d8f29c000  x19 0000000000007e3b
        x20 0000000000007e3b  x21 0000007d8f023020  x22 0000007d8f3b58dc  x23 0000000000000001
        x24 0000007fe73009a0  x25 0000007fe73008e0  x26 0000007fe7300ca0  x27 0000000000000000
        x28 0000000000000000  x29 0000007fe7300c90
        sp  0000007fe7300860  lr  0000007d8ec2f22c  pc  0000007d8ec2f250

    backtrace:
          #00 pc 0000000000088250  /bionic/lib64/libc.so (fdsan_error(char const*, ...)+384)
          #01 pc 0000000000088060  /bionic/lib64/libc.so (android_fdsan_close_with_tag+632)
          #02 pc 00000000000887e8  /bionic/lib64/libc.so (close+16)
          #03 pc 000000000000379c  /system/bin/crasher64 (do_action+1316)
          #04 pc 00000000000049c8  /system/bin/crasher64 (main+96)
          #05 pc 000000000008021c  /bionic/lib64/libc.so (_start_main)
    

この種類の中止は、バックトレースに fdsan_error が存在することと、特定の abort メッセージによって、他の種類の中止から区別できます。

このタイプのクラッシュのインスタンスを再現するには、crasher fdsan_file または crasher fdsan_dir を使用します。

クラッシュ ダンプを調査する

現在調査中の特定のクラッシュがない場合、プラットフォーム ソースには debuggerd をテストするためのクラッシャーというツールが含まれています。system/core/debuggerd/mm を実行すると、パス上の crashercrasher64 の両方が取得されます(後者は 64 ビットのクラッシュをテストできます)。クラッシャーは、指定するコマンドライン引数に基づいて、さまざまな興味深い方法でクラッシュします。crasher --help を使用すると、現在サポートされている選択肢が表示されます。

クラッシュ ダンプのさまざまな部分を調べるため、次のクラッシュ ダンプの例を見てみましょう。

    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
    Revision: '0'
    ABI: 'arm'
    pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'some_file.c:123: some_function: assertion "false" failed'
        r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
        r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
        r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
        ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
    backtrace:
        #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
        #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
        #02 pc 0001bb87  /system/lib/libc.so (raise+10)
        #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
        #04 pc 000168e8  /system/lib/libc.so (abort+4)
        #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
        #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
        #07 pc 00000f21  /system/xbin/crasher
        #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
        #09 pc 00000abc  /system/xbin/crasher
    Tombstone written to: /data/tombstones/tombstone_06
    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    

スペース入りのアスタリスクの行は、ネイティブ クラッシュのログを検索する際に役立ちます。「*** ***」という文字列は、ネイティブ クラッシュの冒頭以外ではまれにしか表示されません。

    Build fingerprint:
    'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
    

フィンガープリントにより、クラッシュが発生したビルドを正確に特定できます。これは、ro.build.fingerprint システム プロパティとまったく同じです。

    Revision: '0'
    

リビジョンは、ソフトウェアではなくハードウェアを指します。これは通常は使用されませんが、ハードウェアの不具合により発生したことがわかっているバグを自動的に無視するのに役立ちます。これは、ro.revision システム プロパティとまったく同じです。

    ABI: 'arm'
    

ABI は、arm、arm64、x86、x86-64 のいずれかです。これは主に上記の stack スクリプトで、使用するツールチェーンを認識するのに役立ちます。

    pid: 1656, tid: 1656, name: crasher >>> crasher <<<
    

この行は、クラッシュしたプロセス内の特定のスレッドを示します。この場合、プロセスのメインスレッドであるため、プロセス ID とスレッド ID は一致します。最初の名前はスレッド名で、>>> と <<< で囲まれた名前はプロセス名です。アプリでは、プロセス名は一般的には完全修飾パッケージ名(com.facebook.katana など)です。これは、バグの報告や Google Play でのアプリの検索に便利です。pid と tid は、クラッシュに先行する関連ログ行を見つける際にも役立ちます。

    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    

この行は、受信されたシグナル(SIGABRT)と、それが受信された方法(SI_TKILL)を示します。debuggerd によってレポートされるシグナルは、SIGABRT、SIGBUS、SIGFPE、SIGILL、SIGSEGV、SIGTRAP です。シグナル固有のコードは、特定のシグナルごとに異なります。

    Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    

すべてのクラッシュに abort メッセージ行があるとは限りませんが、中止には abort メッセージ行があります。これは、この pid/tid の「fatal」logcat 出力の最後の行から自動的に収集されます。意図的な中止の場合、たいていはプログラムが強制終了した理由が表示されます。

    r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8
    r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c
    r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c
    ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010
    

レジスタダンプは、シグナルが受信されたときの CPU レジスタの内容を表示します(このセクションは、ABI によって大きく異なります)。その有用性は、クラッシュごとに異なります。

    backtrace:
        #00 pc 00042c98 /system/lib/libc.so (tgkill+12)
        #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32)
        #02 pc 0001bb87 /system/lib/libc.so (raise+10)
        #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34)
        #04 pc 000168e8 /system/lib/libc.so (abort+4)
        #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16)
        #06 pc 00018d35 /system/lib/libc.so (__assert2+20)
        #07 pc 00000f21 /system/xbin/crasher
        #08 pc 00016795 /system/lib/libc.so (__libc_init+44)
        #09 pc 00000abc /system/xbin/crasher
    

バックトレースは、クラッシュ時のコードの位置を示します。最初の列はフレーム番号です(gdb のスタイルと同じく、最も深いフレームは 0 です)。PC 値は絶対アドレスではなく、共有ライブラリの相対位置です。次の列は、マッピングされた領域の名前です(通常は共有ライブラリまたは実行可能ファイルですが、たとえば JIT コンパイル済みコードではない可能性があります)。最後に、シンボルが使用可能な場合、PC 値が対応するシンボルが、そのシンボルへのオフセット(バイト単位)とともに表示されます。これは、objdump(1) と組み合わせて、対応するアセンブラ命令を検索するために使用できます。

tombstone を調査する

    Tombstone written to: /data/tombstones/tombstone_06
    

この行により、debuggerd がどこに追加情報を書き込んだかがわかります。debuggerd は最大 10 個の tombstone を保持し、00~09 の数字を周期的に使用して、必要に応じて既存の tombstone を上書きします。

tombstone には、クラッシュ ダンプと同じ情報に加えて、いくつかの追加情報が含まれます。たとえば、(クラッシュしたスレッドだけでなく)すべてのスレッド、浮動小数点レジスタ、生のスタックダンプ、レジスタ内のアドレス周辺のメモリダンプなどです。最も有用なのは、(/proc/pid/maps に似た)フルメモリ マップです。以下に、32 ビット ARM プロセスがクラッシュした例をアノテーション付きで示します。

    memory map: (fault address prefixed with --->)
    --->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId:
    b9527db01b5cf8f5402f899f64b9b121)
    

ここで注目すべき点が 2 つあります。1 つ目は、この行にプレフィックス「--->」が付いていることです。クラッシュが単なる null ポインタ逆参照でない場合、マップは大変役立ちます。フォールト アドレスが小さい場合は、おそらく null ポインタ逆参照の一種です。そうでない場合は、フォールト アドレス周辺のマップを見ると、何が起きたかを理解する手がかりを得られることがよくあります。マップを見ると、次のような問題が考えられます。

  • メモリブロックが終わった後の読み取り / 書き込み。
  • メモリブロックが始まる前の読み取り / 書き込み。
  • コード以外を実行しようとした。
  • スタックの終わりの実行。
  • コードに書き込もうとした(上記の例を参照)。

注目すべき 2 つ目の点は、Android 6.0 以上では実行可能ファイルと共有ライブラリ ファイルは BuildId を(もしあれば)表示するため、クラッシュしたコードのバージョンが正確にわかることです。Android 6.0 以降のプラットフォーム バイナリにはデフォルトで BuildId が含まれており、NDK r12 以上では -Wl,--build-id も自動的にリンカーに渡します。

    ab163000-ab163fff r--      3000      1000  /system/xbin/crasher
    ab164000-ab164fff rw-         0      1000
    f6c80000-f6d7ffff rw-         0    100000  [anon:libc_malloc]
    

Android では、ヒープは単一の領域であるとは限りません。ヒープ領域にはラベル [anon:libc_malloc] が付けられます。

    f6d82000-f6da1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
    f6da2000-f6dc1fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
    f6dc2000-f6de1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
    f6de2000-f6de5fff r-x         0      4000  /system/lib/libnetd_client.so (BuildId: 08020aa06ed48cf9f6971861abf06c9d)
    f6de6000-f6de6fff r--      3000      1000  /system/lib/libnetd_client.so
    f6de7000-f6de7fff rw-      4000      1000  /system/lib/libnetd_client.so
    f6dec000-f6e74fff r-x         0     89000  /system/lib/libc++.so (BuildId: 8f1f2be4b37d7067d366543fafececa2) (load base 0x2000)
    f6e75000-f6e75fff ---         0      1000
    f6e76000-f6e79fff r--     89000      4000  /system/lib/libc++.so
    f6e7a000-f6e7afff rw-     8d000      1000  /system/lib/libc++.so
    f6e7b000-f6e7bfff rw-         0      1000  [anon:.bss]
    f6e7c000-f6efdfff r-x         0     82000  /system/lib/libc.so (BuildId: d189b369d1aafe11feb7014d411bb9c3)
    f6efe000-f6f01fff r--     81000      4000  /system/lib/libc.so
    f6f02000-f6f03fff rw-     85000      2000  /system/lib/libc.so
    f6f04000-f6f04fff rw-         0      1000  [anon:.bss]
    f6f05000-f6f05fff r--         0      1000  [anon:.bss]
    f6f06000-f6f0bfff rw-         0      6000  [anon:.bss]
    f6f0c000-f6f21fff r-x         0     16000  /system/lib/libcutils.so (BuildId: d6d68a419dadd645ca852cd339f89741)
    f6f22000-f6f22fff r--     15000      1000  /system/lib/libcutils.so
    f6f23000-f6f23fff rw-     16000      1000  /system/lib/libcutils.so
    f6f24000-f6f31fff r-x         0      e000  /system/lib/liblog.so (BuildId: e4d30918d1b1028a1ba23d2ab72536fc)
    f6f32000-f6f32fff r--      d000      1000  /system/lib/liblog.so
    f6f33000-f6f33fff rw-      e000      1000  /system/lib/liblog.so
    

一般的に、共有ライブラリには 3 つの隣接するエントリがあります。1 つは読み取り可能で実行可能(コード)、1 つは読み取り専用(読み取り専用データ)、もう 1 つは読み取り / 書き込み用(変更可能データ)です。最初の列はマッピングのアドレス範囲を示し、2 番目の列はアクセス許可を示します(一般的な Unix の ls(1) スタイル)。3 番目の列はファイルへのオフセット(16 進数単位)、4 番目の列は領域のサイズ(16 進数単位)、5 番目の列はファイル(または他の領域名)を示します。

    f6f34000-f6f53fff r-x         0     20000  /system/lib/libm.so (BuildId: 76ba45dcd9247e60227200976a02c69b)
    f6f54000-f6f54fff ---         0      1000
    f6f55000-f6f55fff r--     20000      1000  /system/lib/libm.so
    f6f56000-f6f56fff rw-     21000      1000  /system/lib/libm.so
    f6f58000-f6f58fff rw-         0      1000
    f6f59000-f6f78fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
    f6f79000-f6f98fff r--         0     20000  /dev/__properties__/properties_serial
    f6f99000-f6f99fff rw-         0      1000  [anon:linker_alloc_vector]
    f6f9a000-f6f9afff r--         0      1000  [anon:atexit handlers]
    f6f9b000-f6fbafff r--         0     20000  /dev/__properties__/properties_serial
    f6fbb000-f6fbbfff rw-         0      1000  [anon:linker_alloc_vector]
    f6fbc000-f6fbcfff rw-         0      1000  [anon:linker_alloc_small_objects]
    f6fbd000-f6fbdfff rw-         0      1000  [anon:linker_alloc_vector]
    f6fbe000-f6fbffff rw-         0      2000  [anon:linker_alloc]
    f6fc0000-f6fc0fff r--         0      1000  [anon:linker_alloc]
    f6fc1000-f6fc1fff rw-         0      1000  [anon:linker_alloc_lob]
    f6fc2000-f6fc2fff r--         0      1000  [anon:linker_alloc]
    f6fc3000-f6fc3fff rw-         0      1000  [anon:linker_alloc_vector]
    f6fc4000-f6fc4fff rw-         0      1000  [anon:linker_alloc_small_objects]
    f6fc5000-f6fc5fff rw-         0      1000  [anon:linker_alloc_vector]
    f6fc6000-f6fc6fff rw-         0      1000  [anon:linker_alloc_small_objects]
    f6fc7000-f6fc7fff rw-         0      1000  [anon:arc4random _rsx structure]
    f6fc8000-f6fc8fff rw-         0      1000  [anon:arc4random _rs structure]
    f6fc9000-f6fc9fff r--         0      1000  [anon:atexit handlers]
    f6fca000-f6fcafff ---         0      1000  [anon:thread signal stack guard page]
    

Android 5.0 以降では、C ライブラリはほとんどの匿名マッピング領域に名前を付けます。そのため、不明な領域が少なくなります。

    f6fcb000-f6fccfff rw- 0 2000 [stack:5081]
    

[stack:tid] という名前の領域は、指定されたスレッドのスタックです。

    f6fcd000-f702afff r-x         0     5e000  /system/bin/linker (BuildId: 84f1316198deee0591c8ac7f158f28b7)
    f702b000-f702cfff r--     5d000      2000  /system/bin/linker
    f702d000-f702dfff rw-     5f000      1000  /system/bin/linker
    f702e000-f702ffff rw-         0      2000
    f7030000-f7030fff r--         0      1000
    f7031000-f7032fff rw-         0      2000
    ffcd7000-ffcf7fff rw-         0     21000
    ffff0000-ffff0fff r-x         0      1000  [vectors]
    

[vector][vdso] のどちらが表示されるかは、アーキテクチャによって異なります。ARM では [vector] を使用し、他のすべてのアーキテクチャでは [vdso] を使用します。