הסבר על דוחות HWASan

כשכלי HWASan מזהה באג בזיכרון, התהליך מסתיים באמצעות abort(), ומדפיס דוח ב-stderr וב-logcat. כמו כל קריסות קוד מקורי ב-Android, שגיאות HWASan מופיעות ב-/data/tombstones.

דוח לדוגמה

בהשוואה לקריסות רגילות של קוד נייטיב, ב-HWAsan יש מידע נוסף בשדה Abort message בחלק העליון של סטטוס ה-tombstone. לפניכם דוגמה לקריסה מבוססת-ערימת זיכרון. בהערה מוסבר מה עושים במקרה של באגים ב-stack.

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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 הם שגיאות של אי-התאמה בין תגים, כלומר גישה לזיכרון שבה תג של מצביע לא תואם לתג הזיכרון התואם. יכול להיות שמדובר באחד מהפרטים הבאים:

  • גישה מחוץ למגבלות ב-stack או ב-heap
  • שגיאת שימוש לאחר שחרור בערימה
  • שגיאת שימוש לאחר חזרה ב-stack

קטעים

בהמשך מוסבר על כל אחד מהקטעים בדוח HWASan.

שגיאת גישה

מכיל מידע על הגישה השגויה לזיכרון, כולל:

  • סוג הגישה (READ לעומת WRITE)
  • גודל הגישה (כמה בייטים ניסו לגשת אליהם)
  • מספר השרשור של הגישה
  • תגי מצביע וזיכרון (לניפוי באגים מתקדם)

גישה למעקב אחר סטאק

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

הסיבה

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

  • שימוש אחרי תקופת הניסיון בחינם
  • אי-התאמה של תגים ב-Stack, שיכולה להיות שימוש ב-Stack אחרי חזרה, שימוש ב-Stack אחרי היקף או שימוש מחוץ למגבלות
  • זליגת נתונים במאגר הנתונים הזמני של אשכול
  • Overflow גלובלי

מידע על הזיכרון

תיאור של מה ש-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:
חוסר התאמה בתגים של סטאק בדוחות Stack אין הבחנה בין שגיאות של זליגת נתונים (overflow) או מחסור בנתונים (underflow) לבין באגים מסוג שימוש לאחר חזרה (use-after-return). בנוסף, כדי למצוא את הקצאת ה-stack שמקורת השגיאה, נדרש שלב של סימון אופליין. הסבר על דוחות סטאק
Invalid free שימוש אחרי תקופת הניסיון בחינם באג של 'שחרור כפול'. אם זה קורה בזמן סגירת התהליך, יכול להיות שמדובר בהפרת ODR.
<address> is located N bytes inside of M-byte region [<start>, <end>)
freed by thread T0 here:
לא ניתן לתאר את הכתובת זיכרון ששוחרר ללא צורך (זיכרון שלא הוקצה בעבר), או שחרור כפול של זיכרון אחרי שהזיכרון שהוקצה הוצא מהמאגר הפנוי של HWASan.
0x… הוא זיכרון צל של HWAsan זיכרון פנוי לא ידוע, כי האפליקציה ניסתה לפנות זיכרון פנימי של HWASan.

Deallocation stack trace

מעקב לאחור (stack trace) של המיקום שבו הוקצה הזיכרון. האפשרות הזו מוצגת רק עבור באגים מסוג use-after-free או invalid-free. מידע נוסף זמין במאמר סמליזציה.

דוח קריסות של מקבץ הקצאות

מעקב אחר סטאק של המיקום שבו הוקצה הזיכרון. מידע נוסף זמין במאמר סמליזציה.

מידע מתקדם על ניפוי באגים

הדוח HWASan כולל גם מידע מתקדם על ניפוי באגים, כולל (בסדר):

  1. רשימת השרשור בתהליך
  2. רשימת השרשור בתהליך
  3. הערך של תגי הזיכרון ליד הזיכרון הפגום
  4. הדמפ של הרשומות בנקודת הגישה לזיכרון

גרסת dump של תג זיכרון

אפשר להשתמש ב-dump של זיכרון התג כדי לחפש הקצאות זיכרון בקרבת מקום עם אותו תג כמו תג הסמן. התגים האלה יכולים להצביע על גישה מחוץ לתחום עם סטייה גדולה. תג אחד תואם ל-16 בייטים של זיכרון. תג ההפניה הוא 8 הביטים העליונים של הכתובת. נתוני הדמפ של זיכרון התגים יכולים לספק רמזים. לדוגמה, הנתונים הבאים הם זליגת נתונים ממאגר (buffer overflow) שמופיעה בצד שמאל:

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, היתרה של הגודל מאוחסנת בתור תג זיכרון והתג מאוחסן בתור תג גרגיר קצר. בדוגמה הקודמת, אחרי ההקצאה המודגשת בכתב מודגש עם התג ad, יש הקצאה של 84 בייטים לתג 5c: 5 × 16 + 4 = 84.

תג זיכרון אפס (לדוגמה, tags: ad/00 (ptr/mem)) מציין באג של שימוש ב-stack אחרי חזרה.

קובץ dump של הרישום

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

סימבוליזציה

כדי לקבל שמות של פונקציות ומספרי שורות בנתוני מעקב ה-stack (ולקבל שמות של משתנים עבור באגים מסוג 'שימוש אחרי היקף'), צריך לבצע שלב של סימון אופליין.

הגדרה בפעם הראשונה: התקנה של llvm-symbolizer

לדוגמה, צריך להתקין את llvm-symbolizer במערכת ולאפשר גישה אליו דרך $PATH. ב-Debian, אפשר להתקין אותו באמצעות sudo apt install llvm.

קבלת קובצי סמלים

כדי ליצור סמלים, אנחנו דורשים קובצי בינארי לא מדוללים שמכילים סמלים. המיקום שלהם תלוי בסוג ה-build:

  • בגרסאות build מקומיות, קובצי הסמלים נמצאים בתיקייה out/target/product/<product>/symbols/.
  • בגרסאות build של AOSP (לדוגמה, גרסאות build שעברן הפעלה באמצעות Android Flash Tool), הגרסאות האלה נמצאות ב-Android CI. בArtifacts של ה-build יש קובץ ${PRODUCT}-symbols-${BUILDID}.zip.
  • לגרסאות build פנימיות מהארגון, כדאי לעיין במסמכים של הארגון כדי לקבל עזרה בקבלת קובצי סמלים.

סימבוליזציה

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

כדי לעזור לכם להבין באגים ב-stack, HWASan עוקב אחרי מסגרות stack קודמות. המערכת של HWASan לא ממירה אותם לתוכן שאנשים יכולים להבין בדוח הבאג, ולכן נדרש שלב סמליזציה נוסף.

הפרות של ODR

חלק מהבאגים מסוג 'שימוש לאחר שחרור' שדווחו על ידי HWASan עשויים להצביע על הפרה של כלל One Definition Rule‏ (ODR). הפרה של ODR מתרחשת כאשר אותו משתנה מוגדר כמה פעמים באותה תוכנית. המשמעות היא גם שהמשתנה נהרס כמה פעמים, דבר שעלול להוביל לשגיאה מסוג use-after-free.

אחרי הסימולציה, הפרות של ODR מוצגות כשגיאה מסוג 'שימוש לאחר שחרור' עם __cxa_finalize, גם ב-stack של הגישה הלא חוקית וגם ב-stack של freed here. הערך __dl__ZN6soinfo17call_constructorsEv נמצא ב-stack שוקצה כאן, והוא אמור להצביע על המיקום בתוכנית שבו המשתנה מוגדר, גבוה יותר ב-stack.

אפשר להפר את ה-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. שליחת דיווח על באג עם הוראות לשחזור הבעיה, אם אפשר.