כלי ניקוי כתובות

AddressSanitizer (ASan) הוא כלי מבוסס מהדר (compiler) מהיר לזיהוי באגים בזיכרון בקוד נייטיב.

ASan מזהה:

  • גלישת נתונים/מאגר נתונים זמני של ערימה וערימה
  • שימוש בערימה אחרי בחינם
  • שימוש במקבץ מחוץ להיקף
  • מיטה זוגית חופשיה/חינמית

אפשר להפעיל את ASan ב-ARM 32 סיביות ובגרסת 64 סיביות, וגם ב-x86 וב-x86-64. תקורה של המעבד (CPU) של ASan הוא בערך פי 2, תקורה של גודל קוד היא בין 50% לפי 2, ותקורת זיכרון גדולה (בהתאם לדפוסי ההקצאה שלכם, אך בסדר 2x).

Android 10 והסניף הראשי של AOSP ב-AArch64 תמיכה ב-Hardware-AssistSanitizer (HWASan), כלי דומה עם תקורת RAM נמוכה יותר טווח הבאגים שזוהו. שירות HWASan מזהה שימוש בסטאק אחרי החזרה, בנוסף לבאגים שזוהו על ידי ASan.

ל-HWASan יש תקורה דומה לגבי המעבד (CPU) והקוד, אבל התקורה של ה-RAM היא קטנה בהרבה (15%). HWASan אינו קבוע. קיימים רק 256 ערכי תגים אפשריים, ולכן יש ערך קבוע של 0.4% שיש סבירות גבוהה שיחמיץ באג כלשהו. ב-HWASan אין אזורים אדומים מוגבלים בגודלם זיהוי חריגות והסגר עם קיבולת מוגבלת לזיהוי חריגות לאחר השימוש בחינם, כך שלא משנה ל-HWASan כמה גדול נפח הגלישה או כמה זמן עבר הזיכרון הוקצה. זה עוזר לשפר את HWASan מאשר ASan. אפשר לקרוא מידע נוסף על עיצוב של HWASan או מידע על השימוש ב-HWASan ב-Android.

ASan מזהה קריסות/חריגות גלובליות בנוסף לעומס 'ערימה', והוא מהיר עם תקורת זיכרון מינימלית.

במסמך הזה מתואר איך לפתח ולהפעיל חלקים של Android או את כל מכשירי Android עם אסן. אם אתם מפתחים אפליקציית SDK/NDK עם ASan, עיינו במאמר בנושא כלי לחיטוי כתובות במקום זאת.

חיטוי קובצי הפעלה ספציפיים באמצעות ASan

הוספת LOCAL_SANITIZE:=address או sanitize: { address: true } אל כלל ה-build של קובץ ההפעלה. אפשר לחפש בקוד דוגמאות קיימות או למצוא חומרי החיטוי האחרים שזמינים.

כשמזוהה באג, ASan מדפיסה דוח מפורט הפלט אל logcat ואז קורס של התהליך.

חיטוי ספריות משותפות באמצעות ASan

בשל אופן הפעולה של ASan, ספרייה שנבנתה באמצעות ASan יכולה לשמש רק שנוצר באמצעות ASan.

כדי לנקות ספרייה משותפת שבה משתמשים במספר קובצי הפעלה, לא את כולם שנוצרו בעזרת ASan, תצטרכו שני עותקים של הספרייה. הדרך המומלצת לעשות זאת היא להוסיף את הטקסט הבא אל Android.mk של המודול הרלוונטי:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

הפעולה הזו ממקמת את הספרייה ב-/system/lib/asan במקום /system/lib. לאחר מכן מריצים את קובץ ההפעלה באמצעות:

LD_LIBRARY_PATH=/system/lib/asan

אם מדובר בדימוני מערכת, צריך להוסיף את הקטע הבא לקטע המתאים של /init.rc או /init.$device$.rc.

setenv LD_LIBRARY_PATH /system/lib/asan

צריך לוודא שהתהליך משתמש בספריות מ-/system/lib/asan כשהוא קיים, בקריאה /proc/$PID/maps. אם לא, ייתכן שתצטרכו כדי להשבית את SELinux:

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

דוחות קריסות טובים יותר

ASan משתמשת ב-unwinder מהיר מבוסס-פריים כדי להקליט סטאק לעקוב אחרי כל אירוע של הקצאת זיכרון ומיקום עסקה בתוכנית. נפוצים של Android בנוי ללא מצביעי מסגרות. כתוצאה מכך, לעיתים קרובות תקבלו רק מסגרת אחת או שתיים עם משמעות. כדי לפתור את הבעיה, בנה מחדש את הספרייה עם ASan (מומלץ!), או עם:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

או להגדיר את ASAN_OPTIONS=fast_unwind_on_malloc=0 בתהליך הסביבה. האפשרות השנייה עלולה להיות עמוסה מאוד במעבד, בהתאם את העומס.

ייצוג

בהתחלה, הדוחות של ASan מכילים הפניות להיסט בקבצים בינאריים ומשותפים של הספריות. יש שתי דרכים לקבל מידע על קובץ המקור ועל השורה:

  • מוודאים שהקובץ הבינארי llvm-symbolizer נמצא ב-/system/bin. האפליקציה llvm-symbolizer נבנית ממקורות ב- third_party/llvm/tools/llvm-symbolizer.
  • סינון הדוח דרך external/compiler-rt/lib/asan/scripts/symbolize.py סקריפט.

הגישה השנייה יכולה לספק יותר נתונים (כלומר, file:line מיקומים) בגלל הזמינות של ספריות סימבוליות במארח.

ASan באפליקציות

ל-ASan אין אפשרות לראות את קוד Java, אבל היא יכולה לזהות באגים ב-JNI של הספריות. לשם כך, צריך ליצור את קובץ ההפעלה באמצעות ASan, הפנייה הזו היא /system/bin/app_process(32|64). הזה מפעילה את ASan בכל האפליקציות במכשיר בו-זמנית, וזו גם הרבה עומס, אבל מכשיר עם זיכרון RAM בנפח 2GB אמור להתמודד עם הבעיה הזו.

הוספת LOCAL_SANITIZE:=address אל כלל ה-build app_process ב-frameworks/base/cmds/app_process. אפשר להתעלם היעד app_process__asan באותו הקובץ בינתיים (אם עדיין שם כשקראתם את זה).

עריכת הקטע service zygote של המתאים של system/core/rootdir/init.zygote(32|64).rc כדי להוסיף את את השורות הבאות לקטע של השורות המוכנסות שהמקור שלהן הוא class main, וגם בוצעה כניסה באותו סכום:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

פיתוח, סנכרון adb, אתחול מהיר של Flash ומפעילים מחדש.

שימוש במאפיין wrap

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

כדי לעשות זאת, צריך להפעיל את האפליקציה עם הנכס wrap.. הדוגמה הבאה מפעילה את אפליקציית Gmail ב-ASan:

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

בהקשר הזה, asanwrapper משכתב את /system/bin/app_process ל-/system/bin/asan/app_process, שנוצר באמצעות אסן. היא גם מוסיפה /system/lib/asan בתחילת נתיב החיפוש בספרייה הדינמית. בשיטה הזו נעשה שימוש באינסטגרם ספריות מ-/system/lib/asan מקבלות עדיפות על ספריות רגילות ב-/system/lib כשמריצים אותו עם asanwrapper.

אם יתגלה באג, האפליקציה תקרוס והדוח יודפס ב ביומן.

SANITIZE_TARGET

Android 7.0 ואילך כולל תמיכה בבניית פלטפורמת Android כולה עם ASan בבת אחת. (אם אתם מפתחים גרסה מתקדמת יותר מ-Android 9, עדיף להשתמש ב-HWASan).

מריצים את הפקודות הבאות באותו עץ build.

make -j42
SANITIZE_TARGET=address make -j42

במצב הזה, הקובץ userdata.img מכיל ספריות נוספות וחייב להיות הבהוב גם כן למכשיר. משתמשים בשורת הפקודה הבאה:

fastboot flash userdata && fastboot flashall

הדבר יוצר שתי קבוצות של ספריות משותפות: נורמלית ב- /system/lib (ההפעלה הראשונה), ו-ASan עם אינסטרומנטציה /data/asan/lib (השנייה מפעילה). קובצי הרצה מ- ה-build השני יחליף את ה-build מה-build הראשון. עם אינסטלציה ASan קובצי הפעלה מקבלים נתיב חיפוש אחר בספרייה שכולל /data/asan/lib לפני /system/lib באמצעות שימוש ב- /system/bin/linker_asan בPT_INTERP.

מערכת ה-build מאחזרת ספריות אובייקטים מתווכות כאשר הערך של $SANITIZE_TARGET השתנה. פעולה זו מחייבת בנייה מחדש של כל יעדים תוך שמירה על הקבצים הבינאריים המותקנים במסגרת /system/lib.

יש יעדים שלא ניתן ליצור באמצעות ASan:

  • קובצי הפעלה שמקושרים באופן סטטי
  • LOCAL_CLANG:=false יעדים
  • LOCAL_SANITIZE:=false לא מוגדרים כ-ASand עבור SANITIZE_TARGET=address

מתבצע דילוג על הפעלות כאלה ב-build SANITIZE_TARGET. הגרסה מההפעלה הראשונה של ההפעלה נשארה ב-/system/bin.

ספריות כאלה נבנו ללא ASan. הם יכולים להכיל חלק מה-ASan. מספריות סטטיות שעליהן הן תלויות.

מסמכים תומכים