במסמך הזה מוסבר איך להפעיל כלים מיוחדים לניפוי באגים כשמשתמשים ב-AGDE. הכלים האלה יכולים לעזור בזיהוי שגיאות קשות של כתיבה מחדש וזיהום זיכרון.
HWAddress Sanitizer ו-Address Sanitizer
HWAddress Sanitizer (HWASan) ו-Address Sanitizer (ASan) הם כלים לניפוי באגים של פגיעה בזיכרון, שעוזרים לנפות באגים של פגיעה בזיכרון ושגיאות של כתיבה מעל נתונים, כמו:
- זליגות וחוסר מקום במאגרים של סטאק
- זליגות וחוסר מקום במאגר של אשכול (heap)
- שימוש ב-Stack מחוץ להיקף שלו
- שגיאות של זיכרון פנוי כפול ושגיאות של זיכרון פראי
- שימוש ב-stack אחרי החזרה (HWASan בלבד)
מומלץ להפעיל את HWASan או את ASan רק כשמנפים באגים או כחלק מבדיקות אוטומטיות. הכלים האלה יעילים, אבל השימוש בהם כרוך בעונש.
התנהגות בזמן ריצה
כשהן מופעלות, גם HWASan וגם ASan בודקות באופן אוטומטי אם יש פגיעה בזיכרון באפליקציה.
אם מזוהה שגיאה בזיכרון, האפליקציה קורסת עם השגיאה SIGBART
(signal abort) ומדפיסה הודעה מפורטת ב-logcat. עותק של ההודעה נכתב גם בקובץ ב-/data/tombstones
.
הודעת השגיאה נראית כך:
ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
#0 0x7b24d90a08 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
#1 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
#2 0x7b8f1db364 (/apex/com.android.art/lib64/libart.so+0x18f364)
#3 0x7b8f2ad8d4 (/apex/com.android.art/lib64/libart.so+0x2618d4)
0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
#0 0x7b92a322bc (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
#1 0x7b24d909e0 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
#2 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
דרישות מוקדמות
הדרישות של HWASan
כדי להשתמש ב-HWASan:
- צריך להשתמש ב-AGDE בגרסה 24.1.99 ואילך.
- האפליקציה צריכה להיות מפותחת באמצעות NDK מגרסה 26 ואילך.
- האפליקציה צריכה להיות מפותחת עם target SDK בגרסה 34 ואילך.
- היעד חייב להיות מכשיר
arm64-v8a
עם Android 14 (רמת API 34) ואילך.
שימוש בספרייה הסטנדרטית המשותפת של C++ בפרויקט
עקב בעיה ידועה, ASan לא תואם לטיפול בחריגות ב-C++ כשמשתמשים ב-libc++_static
. הבעיה הזו לא מתרחשת כשמשתמשים ב-libc++_shared
.
ל-HWASan יש הטמעה משלו של האופרטורים new
ו-delete
, שאי אפשר להשתמש בהם אם הספרייה הרגילה מקושרת באופן סטטי לפרויקט.
כדי לשנות את ההגדרה הזו, אפשר לעיין בקטע קישור לספרייה הרגילה של C++ במסמך הזה.
הפעלת יצירת Frame Pointer
ב-HWASan וב-ASan נעשה שימוש ב-unwinder מהיר שמבוסס על מצביע מסגרת כדי ליצור מידע על ניתוח סטאק לאירועים של הקצאת זיכרון וביטול הקצאת זיכרון. כלומר, כדי להשתמש בתכונות האלה, צריך להפעיל את היצירה של מצביע מסגרת בהגדרות של המהדר C++. כלומר, צריך להשבית את האופטימיזציה של השמטת מצביע הפריים.
כדי לשנות את ההגדרה הזו, אפשר לעיין בקטע הפעלת היצירה של Frame Pointer במסמך הזה.
הגדרת הפרויקט ב-Visual Studio לשימוש ב-HWAsan או ב-ASan
הפעלת HWASan או ASan
כדי להפעיל את HWASan או את ASan, עוברים אל Configuration Properties (מאפייני תצורה) > General (כללי) בדפי הנכס של הפרויקט.
איור 1: האפשרות Properties של הפרויקט בחלון Visual Studio Solution Explorer.
איור 2: ההגדרה Address Sanitizer (ASan) במאפיינים הכלליים של הפרויקט.
כדי להפעיל את HWASan בפרויקט, משנים את ההגדרה Address Sanitizer (ASan) ל-Hardware ASan Enabled (fsanitize=hwaddress).
כדי להפעיל את ASan בפרויקט, משנים את ההגדרה Address Sanitizer (ASan) ל-ASan Enabled (fsanitize=address).
הפעלת האפשרות ליצירת Frame Pointer
היצירה של Frame Pointer נשלטת על ידי ההגדרה Omit Frame Pointer במהדר C/C++. אפשר למצוא אותה בדפי הנכסים של הפרויקט בקטע Configuration Properties > C/C++ > Optimization.
איור 3: המיקום של ההגדרה Omit Frame Pointer.
כשמשתמשים ב-HWAsan או ב-ASan, מגדירים את ההגדרה Omit Frame Pointer לערך No (-fno-omit-frame-pointer).
קישור של ספריית התקן של C++ במצב ספרייה משותפת
הגדרת מצב הקישור לספרייה הסטנדרטית של C++ נמצאת בדפי הנכסים של הפרויקט, בקטע Configuration Properties (מאפייני תצורה) > General (כללי), בקטע Project Defaults (ברירת המחדל של הפרויקט).
איור 4: איפה נמצאת הגדרת מצב הקישור לספרייה הרגילה של C++.
כשמשתמשים ב-HWAsan או ב-ASan, מגדירים את Use of STL ל-Use C++ Standard Libraries (.so). הערך הזה מקשר את הספרייה הרגילה של C++ לפרויקט כספרייה משותפת, שנדרשת כדי ש-HWASan ו-ASan יפעלו בצורה תקינה.
יצירת הגדרות build לשימוש ב-Address Sanitizer
אם אתם מעדיפים להשתמש ב-HWAsan או ב-ASan באופן זמני, יכול להיות שלא כדאי ליצור הגדרת build חדשה רק לצורך השימוש בהם. זה יכול לקרות אם הפרויקט שלכם קטן, אם אתם בודקים את התכונה או בתגובה לבעיה שגיליתם במהלך הבדיקה.
עם זאת, אם אתם מוצאים את הכלי שימושי ואתם מתכננים להשתמש בו באופן קבוע, כדאי ליצור הגדרת build חדשה ל-HWAsan או ל-ASan, כפי שמתואר בדוגמה של Teapot. כדאי לעשות זאת, למשל, אם אתם מריצים את Address Sanitizer באופן קבוע כחלק מבדיקות היחידה, או במהלך בדיקות 'עישון' של המשחק שנערכות במשך הלילה.
יצירת הגדרת build נפרדת עשויה להיות שימושית במיוחד אם יש לכם פרויקט גדול שמשתמש במספר גדול של ספריות צד שלישי שדרושה לקשר אותן באופן סטטי לספרייה הרגילה של C++. הגדרות build ייעודיות יכולות לעזור לוודא שהגדרות הפרויקט יהיו מדויקות תמיד.
כדי ליצור הגדרת build, בדפי הנכסים של הפרויקט, לוחצים על הלחצן Configuration Manager… (מנהל ההגדרות האישיות…) ואז פותחים את התפריט הנפתח Active solution configuration (הגדרת הפתרון הפעיל). לאחר מכן בוחרים באפשרות
שימוש ב-HWASan עם מקצאי זיכרון מותאמים אישית
HWASan מיירט באופן אוטומטי את הזיכרון שהוקצה באמצעות malloc
(או new
) כדי שתהיה לו אפשרות להחדיר תגים למצביעים ולבדוק אם יש אי-התאמות בתגים.
עם זאת, כשמשתמשים במקצה זיכרון מותאם אישית, ל-HWAsan אין אפשרות ליירט באופן אוטומטי את שיטות הקצאת הזיכרון בהתאמה אישית. לכן, אם רוצים להשתמש ב-HWAsan עם מנהל הזיכרון המותאם אישית, צריך להוסיף למנהל הזיכרון כלי למעקב כדי שיפעיל את HWASan באופן מפורש. אפשר לעשות זאת באמצעות כמה שורות קוד בלבד.
דרישות מוקדמות
השיטות של HWASan שצריך להפעיל מוגדרות בכותרת הזו:
#include "sanitizer/hwasan_interface.h"
הוספת מכשיר למעקב לשיטת הקצאת הזיכרון
הקצאת אובייקטים ברמת פירוט ויישור של בלוקים של 16 בייטים. לדוגמה, אם יש לכם מנהל מאגר שמספק אובייקטים בגודל קבוע של 24 בייטים, כדאי לעגל את ההקצאות ל-32 בייטים וליישר אותן ל-16 בייטים.
יוצרים תג 8 ביט. אסור להשתמש בערכי התג 0 עד 16, כי הערכים האלה שמורים לשימוש פנימי.
מפעילים את HWASan כדי להתחיל לעקוב אחרי אזור הזיכרון באמצעות התג הזה:
__hwasan_tag_memory((void*) address, tag, size);
מזריקים את התג ל-8 הביטים העליונים של הסמן:
address = __hwasan_tag_pointer((void*) address, tag);
הוספת מכשיר למעקב לשיטה שלכם לביטול הקצאת זיכרון
מאפסים את התג של אזור הזיכרון כדי לגרום לכישלון של גישה נוספת דרך הצבעים המתויגים הקיימים:
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
עבודה עם מאגר אובייקטים שהוקצה מראש
אם מנהל הקצאת הזיכרון מקצה מראש אובייקטים במאגר ומחזיר אותם למאגר במקום לפנות אותם בפועל, שיטת הפטור מאחריות יכולה לשכתב ישירות את התג של הזיכרון ואת הפונקציה עם ערך חדש:
```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```
אם משתמשים בשיטה הזו, שיטות ההקצאה לא צריכות לתייג את הפונקציות להצבעה או את בלוקי הזיכרון, אלא לתייג את הפונקציות להצבעה ואת בלוקי הזיכרון כשמקצים מראש את האובייקטים במאגר. דוגמה לשימוש בסגנון הזה מופיעה ב-PoolAllocator sample.