אבחון קריסות מקוריות

הקטעים הבאים כוללים סוגים נפוצים של קריסות מחשב, ניתוח של דגימת קריסה לדוגמה ודיון על מצבות. כל סוג קריסה כולל פלט debuggerd לדוגמה עם הדגשה של הוכחות מרכזיות כדי לעזור ניתן להבדיל בין סוג הקריסה הספציפי.

ביטול

הפלות מעניינות כי הן מכוונות. יש הרבה דרכים שונות לביטול (כולל התקשרות abort(3), כשל assert(3), באמצעות אחד מסוגי הרישום הקטלניים שספציפיים ל-Android), אבל כולן כפופות מתקשרת אל abort. שיחה למספר abort מסמנת את השיחה שרשור עם SIGABRT, כך שמסגרת עם הכיתוב 'בטל' בעוד libc.so SIGABRT הם הדברים שצריך לבדוק בפלט debuggerd כדי להכיר את המקרה הזה.

יכול להיות שיש 'הודעת ביטול' מפורשת השורה הזו. כדאי גם לעיין פלט של logcat, כדי לראות מה תועד השרשור הזה לפני כן שהוא הורג את עצמו, כי בניגוד לassert(3) או לרמה גבוהה של מקרי מוות מתקני רישום ביומן, abort(3) לא מקבלים הודעות.

הגרסאות הנוכחיות של Android כוללות את tgkill(2) אז את המקבצים שלהם הכי קל לקרוא, לבטל(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 עברו נתיב מפותל בין הגרסה המקורית מבטלים את הקריאה (מסגרת 4 כאן) ואת השליחה בפועל של האות (מסגרת 0 כאן). זה היה נכון במיוחד ב-ARM 32-bit, __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, אלא מספר נמוך אחר. שניים או ברוב המקרים, כתובות שמורכבות מ-3 ספרות הן תופעה נפוצה מאוד, היא כמעט בהחלט לא ביטול הפניה של מצביע null - נדרש היסט של 1MiB. זה קורה בדרך כלל כשיש לכם קוד מבטל את ההפניה של מצביע 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 כן מפנה את state של ה-pthread_mutex_t* שניתנה לו. אם תסתכלו source, אפשר לראות שרכיב מסוים נמצא בקיזוז 0 במבנה, מה שאומר שהשדה pthread_mutex_lock קיבל את הסמן הלא חוקי 0xc. מאת מסגרת 1 אפשר לראות שהוא קיבל את הסמן הזה על ידי readdir, שמחלץ את השדה mutex_ מה-DIR* שניתנו. במבט על המבנה הזה, אפשר לראות ש-mutex_ נמצאת לקזז את sizeof(int) + sizeof(size_t) + sizeof(dirent*) עם struct DIR, שבמכשיר 32 ביט הוא 4 + 4 + 4 = 12 = 0xc, כך מצאת את הבאג: מצביע null הועבר על ידי readdir של הקריאה החוזרת. בשלב הזה תוכלו להדביק את המקבץ בכלי המקבצים איפה ב-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 רבות פונקציות מבוזרות; הם לוקחים ארגומנט נוסף שאומר להם מה הגודל בפועל של מאגר הנתונים הזמני ובודקים בזמן הריצה אם הפעולה מתבצעת שמנסים לבצע. הנה דוגמה שבה הקוד מנסה ל-read(fd, buf, 32) במאגר נתונים זמני שלמעשה הוא 10 בייטים בלבד ארוך...

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 במעקב האחורי ובהודעת הביטול הספציפית.

אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher smash-stack.

Seccomp SIGSYS מקריאת מערכת אסורה

רכיב seccomp (ספציפית, seccomp-bpf) מגבילה את הגישה לקריאות מערכת. לקבלת מידע נוסף מידע על seccomp למפתחי פלטפורמות, ראה את הפוסט בבלוג Seccomp סינון ב-Android O. שרשור שמפעיל שיחת מערכת מוגבלת מקבלים אות SIGSYS עם הקוד SYS_SECCOMP. המספר להתקשרות במערכת יהיה שמוצגת בשורת הסיבה, יחד עם הארכיטקטורה. חשוב לציין שמספרי השיחות של המערכת משתנים בין ארכיטקטורות. לדוגמה, שיחת המערכת של readlinkat(2) היא מספר 305 ב-x86 אבל 267 ב-x86-64. מספר השיחה שונה שוב בזרוע ובזרוע 64. כי שיחת מערכת יכולים להיות הבדלים בין הארכיטקטורות, אבל בדרך כלל קל יותר להשתמש בדוח הקריסות כדי לגלות איזו קריאת מערכת לא אושרה במקום לחפש את מספר שיחת מערכת בכותרות.

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 בלבד)

בזרוע 64 ב-Android 10 בלבד, פלחים להפעלה של מופו קבצים בינאריים וספריות לזיכרון הרצה בלבד (לא קריא) כשיטת הקשחה והגנה מפני מתקפות של שימוש חוזר בקוד. ההקלות האלה לא הייתה קשורות למיטיגציות אחרות, ולכן הן הוסרו.

כאשר קוד לא קריא, הוא גורם לקריאות מכוונות ולא מכוונות לקטעי זיכרון מסומנים הפעלה בלבד כדי לזרוק SIGSEGV עם הקוד SEGV_ACCERR. יכול להיות כתוצאה מבאג, נקודת חולשה, נתונים מעורבים עם קוד (למשל מאגר מילולי), או בחינה עצמית מכוונת של הזיכרון.

המהדר מניח שהקוד והנתונים לא מתערבבים, אבל יכולות להיווצר בעיות כתוצאה מכתיבה ידנית להרכיב. במקרים רבים ניתן לתקן את המצבים על ידי העברת הקבועים אל .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

הכלי לחיטוי קבצים של fdsan ב-Android עוזר לזהות טעויות נפוצות באמצעות תיאורי קבצים כשימוש אחרי סגירה וסגירה כפולה. לצפייה 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 במעקב האחורי ובהודעת הביטול הספציפית.

אפשר לשחזר מופע של קריסה מהסוג הזה באמצעות crasher fdsan_file או crasher fdsan_dir.

חקירת קובצי קריסה

אם אין קריסה ספציפית שאתם בודקים כרגע, מקור הפלטפורמה כולל כלי לבדיקת debuggerd שנקרא קריסה. אם mm בsystem/core/debuggerd/ תקבל מקבלים גם crasher וגם crasher64 בנתיב כך שתוכלו לבדוק קריסות של 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'

טביעת האצבע מאפשרת לזהות בדיוק באיזה גרסת build התרחשה הקריסה. זאת בדיוק אותה מערכת של ro.build.fingerprint לנכס.

Revision: '0'

הגרסה הקודמת מתייחסת לחומרה ולא לתוכנה. בדרך כלל לא בשימוש, אבל יכול לעזור לכם להתעלם באופן אוטומטי מבאגים שידועים נגרמו על ידי חומרה בעייתית. זה בדיוק כמו מאפיין מערכת אחד (ro.revision).

ABI: 'arm'

ה-ABI הוא אחד מהשניים, arm64 , x86 או x86-64. זה בעיקר שימושי לסקריפט stack שהוזכר למעלה, כדי שהוא ידע באיזה כלי להשתמש.

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

השורה הזו מזהה את השרשור הספציפי בתהליך שבו קרס. כאן זה היה התהליך, כך שמזהה התהליך ומזהה ה-thread תואמים. השם הפרטי הוא שם השרשור, והשם מוקף בתחילית >>>. וגם <<< הוא שם התהליך. עבור אפליקציה, שם התהליך הוא בדרך כלל שם החבילה המוגדר במלואו (כגון com.facebook.katana), שהוא שימושי כאשר שולחים באגים או מנסים למצוא את האפליקציה ב-Google Play. ה-PID ו- גם באיתור שורות היומן הרלוונטיות שקדמו לקריסה,

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'

לא לכל הקריסות תופיע שורת הודעה, אבל הן יבוטלו. הדבר נאסף אוטומטית מהשורה האחרונה של פלט 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

ב-Dump הרישום מוצג התוכן של המעבד (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). לבסוף, אם יש סמלים זמינים, זהו הסמל של המחשב תואם מוצג, יחד עם ההיסט של הסמל הזה בייטים. אפשר להשתמש במדיניות הזו בשילוב עם objdump(1) כדי למצוא את ההוראות המתאימות ל-Assmbler.

קריאת מצבות

Tombstone written to: /data/tombstones/tombstone_06

כאן אפשר לראות איפה מידע נוסף נכתב על ידי debuggerd. debuggerd תשמור עד 10 מצבות, והחלפה של מצבות קיימות לפי הצורך.

המצבה מכילה את אותו מידע כמו תמונת המצב של התאונה, בתוספת כמה תוספות. לדוגמה, הוא כולל מעקבים לאחור עבור כל השרשורים (לא רק השרשור שקורס), הנקודה הצפה מתעדכנת, מצבי ערימה גולמיים, וזרמי זיכרון מסביב לכתובות ברישומים. התכונה המועילה ביותר היא גם כולל מפת זיכרון מלאה (בדומה ל-/proc/pid/maps). הנה דוגמה עם הערות לגבי קריסת תהליך ARM בגרסת 32 סיביות:

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

יש שני דברים שחשוב לציין כאן. הראשונה היא שהקידומת של השורה הזו עם "--->". המפות הכי שימושיות כשהקריסה לא רק אפסית ביטול ההפניה של הסמן. אם הכתובת שגויה, כנראה מדובר בוריאציה של ביטול הפניה של מצביע null. אחרת, הסתכלות במפות מסביב לפגמים לרוב יכולה לרמוז לכם על מה שקרה. כמה בעיות אפשריות שאותם ניתן לזהות באמצעות עיון במפות:

  • קריאה/כתיבה מעבר לסוף של בלוק זיכרון.
  • קריאה/כתיבה לפני ההתחלה של בלוק זיכרון.
  • ניסיונות להריץ נתונים ללא קוד.
  • סיכום של מקבץ תמונות.
  • ניסיונות לכתוב לקוד (כמו בדוגמה שלמעלה).

הדבר השני שיש לציין הוא שקובצי הפעלה וקובצי ספריות משותפות יש להציג את ה-BuildId (אם קיים) ב-Android 6.0 ואילך, כדי שתוכל לראות בדיוק איזו גרסה של הקוד קרסה. הקבצים הבינאריים של הפלטפורמות כוללים BuildId לפי ברירת מחדל החל מ-Android 6.0; NDK r12 ומעלה עובר אוטומטית -Wl,--build-id גם ל-linker.

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

ב-Android, הערימה היא לא בהכרח אזור אחד. אזורי ערימה (heap) יופיע בתווית [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

בדרך כלל, לספרייה משותפת יש שלוש רשומות סמוכות. אחד מהם קריא קובץ הפעלה (קוד), אחד הוא לקריאה בלבד (נתונים לקריאה בלבד) והשני הוא קריאה-כתיבה (נתונים שניתנים לשינוי). בעמודה הראשונה מוצגים טווחי הכתובות שמשמשים למיפוי, העמודה השנייה הרשאות (בסגנון הרגיל של Unix ls(1)), בעמודה השלישית את ההיסט של הקובץ (בהקסדצימלי), העמודה הרביעית היא הגודל של האזור (בהקסדצימלי), ובעמודה החמישית של הקובץ (או בשם האזור האחר).

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].