Scudo הוא מנהל זיכרון דינמי במצב משתמש, או מנהל ערימת, שנועד להיות עמיד בפני נקודות חולשה שקשורות לערימת זיכרון (כמו overflow של מאגר מבוסס-ערימת, שימוש אחרי שחרור ושחרור כפול) תוך שמירה על הביצועים. הוא מספק את הרכיבים הסטנדרטיים של הקצאה והקצאה (כמו malloc ו-free), וכן את רכיבי C++ (כמו חדש ו-Delete).
Scudo הוא יותר אמצעי לצמצום נזקים מאשר גלאי שגיאות זיכרון מלא כמו AddressSanitizer (ASan).
החל מהגרסה ל-Android 11, scudo משמש לכל קודי ה-Native (למעט במכשירים עם נפח זיכרון נמוך שבהם עדיין משתמשים ב-jemalloc). בזמן הריצה, Scudo מטפל בכל ההקצאות והביטולים של זיכרון אשכול מקומי לכל קובצי ההפעלה והספריות שלהם, והתהליך מופסק אם מזוהה פגיעה או התנהגות חשודה באשכול.
Scudo הוא קוד פתוח והוא חלק מפרויקט compiler-rt של LLVM. המסמכים זמינים בכתובת https://llvm.org/docs/ScudoHardenedAllocator.html. סביבת זמן הריצה של Scudo מגיעה כחלק מ-Android toolchain, והתמיכה נוספה ל-Soong ו-Make כדי לאפשר הפעלה קלה של המקצה בקובץ בינארי.
אפשר להפעיל או להשבית את הפחתת העומס הנוספת במקצה באמצעות האפשרויות שמתוארות בהמשך.
התאמה אישית
אפשר להגדיר חלק מהפרמטרים של המקצה על בסיס תהליך, בכמה דרכים:
- סטטית: מגדירים פונקציית
__scudo_default_options
בתוכנית שמחזירה את מחרוזת האפשרויות לניתוח. אב הטיפוס של הפונקציה הזו צריך להיות:extern "C" const char *__scudo_default_options()
. - דינמי: משתמשים במשתנה הסביבה
SCUDO_OPTIONS
שמכיל את מחרוזת האפשרויות לניתוח. אפשרויות שהוגדרו באופן כזה מבטלות כל הגדרה שנוצרה באמצעות__scudo_default_options
.
אלו האפשרויות הזמינות.
אפשרות | ברירת המחדל של 64 ביט | ברירת מחדל של 32 ביט | תיאור |
---|---|---|---|
QuarantineSizeKb |
256 |
64 |
הגודל (ב-KB) של המרחב לכליאת קבצים שמשמש לעיכוב הפעולה בפועל של ביטול ההקצאה של קטעי קוד. ערך נמוך יותר עשוי להפחית את השימוש בזיכרון אבל להפחית את יעילות ההפחתה. ערך שלילי יחזור לברירות המחדל. אם מגדירים גם את הערך הזה וגם את הערך של ThreadLocalQuarantineSizeKb לאפס, ההסגר מושבת לגמרי. |
QuarantineChunksUpToSize |
2048 |
512 |
הגודל (בבייטים) שעד אליו אפשר להעביר מקטעים להסגר. |
ThreadLocalQuarantineSizeKb |
64 |
16 |
הגודל (ב-KB) של השימוש במטמון לכל חוט כדי להפחית את העומס בבידוד הגלובלי.
ערך נמוך יותר עשוי להפחית את השימוש בזיכרון, אבל עלול להגדיל את המאבק על המרחב המבודד הגלובלי. הגדרה של הערך הזה ושל הערך QuarantineSizeKb לאפס משביתה את הבידוד לחלוטין. |
DeallocationTypeMismatch |
false |
false |
הפעלת דיווח על שגיאות ב-malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
הפעלת דיווח על שגיאות במקרה של אי התאמה בין הגדלים של הקבצים החדשים והקבצים שנמחקו. |
ZeroContents |
false |
false |
הפעלת תוכן של אפס קטעים במהלך הקצאה וביטול הקצאה. |
allocator_may_return_null |
false |
false |
מציין שהמקצה יכול להחזיר null כשמתרחשת שגיאה שניתן לשחזור, במקום לסיים את התהליך. |
hard_rss_limit_mb |
0 |
0 |
כשה-RSS של התהליך מגיע למגבלה הזו, התהליך מסתיים. |
soft_rss_limit_mb |
0 |
0 |
כשה-RSS של התהליך מגיע למגבלה הזו, הקצאות נוספות נכשלות או מחזירות את הערך null (בהתאם לערך של allocator_may_return_null ), עד שה-RSS יורד שוב כדי לאפשר הקצאות חדשות. |
allocator_release_to_os_interval_ms |
לא רלוונטי | 5000 |
השפעה רק על מנהל זיכרון בנפח 64 ביט. אם הערך מוגדר, המערכת תנסה לפנות זיכרון לא מנוצל למערכת ההפעלה, אבל לא בתדירות גבוהה יותר מהמרווח הזה (במילי-שניות). אם הערך של המאפיין הוא שלילי, המערכת לא משחררת את הזיכרון למערכת ההפעלה. |
abort_on_error |
true |
true |
אם היא מוגדרת, הכלי יקרא abort() במקום _exit() אחרי הדפסת הודעת השגיאה. |
אימות
בשלב זה אין בדיקות CTS ספציפיות ל-Scudo. במקום זאת, צריך לוודא שבדיקות CTS עוברות עם הפעלת Scudo או בלי הפעלת Scudo עבור קובץ בינארי נתון, כדי לוודא שהוא לא משפיע על המכשיר.
פתרון בעיות
אם מזוהה בעיה שאי אפשר לשחזר, המקצה מציג הודעת שגיאה לתיאור השגיאה הסטנדרטי ואז מסיים את התהליך.
נתוני מעקב ה-stack שמובילים לסיום הפעילות מתווספים ליומן המערכת.
הפלט בדרך כלל מתחיל ב-Scudo ERROR:
ואחריו סיכום קצר של הבעיה, יחד עם הנחיות.
ריכזנו כאן רשימה של הודעות השגיאה הנוכחיות והסיבות האפשריות שלהן:
corrupted chunk header
: אימות סיכום הביקורת של כותרת המקטע נכשל. הסיבה לכך היא אחת משתי אפשרויות: הכותרת נכתבה מחדש (חלקית או באופן מלא), או שהמעצבת שהועברה לפונקציה היא לא מקטע.race on chunk header
: שני שרשורים שונים מנסים לבצע מניפולציה על אותה כותרת בו-זמנית. בדרך כלל, זהו סימן לתנאי מרוץ (race condition) או לנעילה כללית לא תקינה בזמן ביצוע פעולות בחלק הזה.invalid chunk state
: הקטע לא במצב הצפוי לפעולה מסוימת. לדוגמה, הוא לא הוקצה כשניסיתם לפנות אותו, או שהוא לא הועבר לבידוד כשניסיתם למחזר אותו. הסיבה העיקרית לשגיאה הזו היא ביטול כפול של משאב.misaligned pointer
: יש אכיפה חזקה של דרישות ההתאמה הבסיסית: 8 בייטים בפלטפורמות של 32 ביט ו-16 בייטים בפלטפורמות של 64 ביט. אם מצביע שהוענק לפונקציות שלנו לא תואם לתנאים האלה, סימן שהצבען שהוענק לאחת מהפונקציות לא מיושר.allocation type mismatch
: כשהאפשרות הזו מופעלת, פונקציית transactionlocation שנקראת במקטע צריכה להיות תואמת לסוג הפונקציה שנשלחה כדי להקצות אותו. אי-התאמה כזו עלולה לגרום לבעיות אבטחה.invalid sized delete
: כשמשתמשים באופרטור מחיקה בגודל C++14 והבדיקה האופציונלית מופעלת, יש חוסר התאמה בין הגודל שהועבר במהלך הקצאת המקטע לבין הגודל המבוקש בזמן ההקצאה. בדרך כלל מדובר בבעיה במהדר או בבלבול בסוגים של האובייקט שרוצים לבטל את ההקצאה שלו.RSS limit exhausted
: חרגתם ממספר הפריטים המקסימלי ב-RSS שצוין.
אם אתם מנסים לנפות באגים של קריסה במערכת ההפעלה עצמה, תוכלו להשתמש בגרסת build של HWASan OS. אם אתם מנסים לנפות באגים של קריסה באפליקציה, תוכלו להשתמש גם בגרסת build של אפליקציה ב-HWASan.