סקודו

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.