בקרה על תקינות התהליך

נכון ל-2016, כ-86% מכל נקודות החולשה ב-Android הן בטיחות זיכרון קשורות. רוב נקודות החולשה מנצלים את רוב נקודות החולשה על ידי תוקפים שמשנים את השגרה לשלוט בזרימה של אפליקציה כדי לבצע פעילויות זדוניות שרירותיות את כל ההרשאות של האפליקציה שנוצלה. תהליך בקרה תקינות (CFI) היא מנגנון אבטחה שלא מאפשר לבצע שינויים תרשים זרימת הבקרה המקורי של בינארי שעבר הידור, מה שהופך את התהליך לקשה יותר באופן משמעותי לביצוע התקפות כאלה.

ב-Android 8.1, הפעלנו את ההטמעה של CFI ב-LLVM בסטאק המדיה. לחשבון ב-Android 9, אפשרנו CFI ביותר רכיבים וגם בליבה. CFI של המערכת הוא מופעל כברירת מחדל, אבל צריך להפעיל CFI ליבה (kernel).

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

לקבלת פרטים טכניים נוספים על CFI ועל האופן שבו מתבצעות בדיקות אחרות של בקרת העברה ראו את העיצוב של LLVM תיעוד.

דוגמאות ומקור

ה-CFI מסופק על ידי המהדר ומוסיף אינסטרומנטציה לקובץ הבינארי במהלך זמן הידור. אנחנו תומכים ב-CFI ב-Clang Toolchain ובמערכת ה-build של Android ב-AOSP.

CFI מופעל כברירת מחדל במכשירי Arm64 עבור קבוצת הרכיבים /platform/build/target/product/cfi-common.mk הוא גם מופעל ישירות בקבוצה של רכיבי מדיה קובצי cookie/תוכנית קבצים, כמו /platform/frameworks/av/media/libmedia/Android.bp ו-/platform/frameworks/av/cmds/stagefright/Android.mk.

הטמעת CFI של המערכת

CFI מופעל כברירת מחדל אם משתמשים ב-Clang ובמערכת ה-build של Android. CFI עוזר לשמור על הבטיחות של משתמשי Android, ולכן לא מומלץ להשבית אותו.

למעשה, מומלץ מאוד להפעיל CFI לרכיבים נוספים. המועמדים האידיאליים הם קוד נייטיב בעל הרשאות או קוד נייטיב שמעבד קלט לא מהימן של משתמשים. אם משתמשים ב-clang ובמערכת ה-build של Android, הוא יכול להפעיל CFI ברכיבים חדשים על ידי הוספת כמה שורות ל-Makefile או קובצי שרטוטים.

תמיכה ב-CFI ב-makefiles

כדי להפעיל CFI בקובץ יצרן כמו /platform/frameworks/av/cmds/stagefright/Android.mk, add:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt

  • LOCAL_SANITIZE מציין CFI כחומר לחיטוי במהלך build.
  • מצב אבחון ל-CFI מופעל על ידי LOCAL_SANITIZE_DIAG. מצב האבחון מדפיס מידע נוסף על תוצאות ניפוי הבאגים ב-Logcat במהלך קריסה. היא שימושית במהלך פיתוח ובדיקה של גרסאות ה-build. יצרן אבל רצוי להסיר את מצב האבחון בגרסאות build של הפקות.
  • LOCAL_SANITIZE_BLACKLIST מאפשר לרכיבים באופן סלקטיבי להשבית אינסטרומנטציה של CFI לפונקציות בודדות או בקובצי מקור. שלך יכול להשתמש ברשימה שחורה כמוצא אחרון לתיקון כל בעיה הקשורה למשתמשים עשוי להתקיים אחרת. פרטים נוספים זמינים במאמר השבתת CFI.

תמיכה ב-CFI בקובצי תוכנית

כדי להפעיל CFI בקובץ תוכנית, כמו /platform/frameworks/av/media/libmedia/Android.bp, add:

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

פתרון בעיות

אם אתם מפעילים CFI ברכיבים חדשים, יכול להיות שתיתקלו בכמה בעיות שגיאות בחוסר התאמה של סוגי הפונקציה ואי התאמה בסוג קוד ההרכבה .

מתרחשות שגיאות של חוסר התאמה בסוג הפונקציה כי CFI מגביל קריאות עקיפות לפונקציות עקיפות בלבד מעבר לפונקציות בעלות סוג דינמי זהה לפונקציות הסטטיות שבהן נעשה שימוש שיחה. CFI מגביל קריאות לפונקציות חברים וירטואליות ולא וירטואליות כדי לדלג רק על הפעלות לאובייקטים שהם מחלקה נגזרת מהסוג הסטטי של האובייקט שמשמש להתקשר. כלומר, כשיש לכם קוד שמפר אחד מהשניים הנחות, האינסטרומנטציה ש-CFI מוסיף יבוטלו. לדוגמה, דוח קריסות מראה SIGABRT ו-Logcat מכיל שורה על תהליך הבקרה של מציאת אי-התאמה.

כדי לפתור את הבעיה, ודאו שהפונקציה נקראת מאותו סוג מוצהר באופן סטטי. הנה שני ערכי CL לדוגמה:

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

כדי לפתור את הבעיה, צריך ליצור רכיבי wrapper של קוד מקורי לכל קריאה להרכבה, ולתת את הפקודה wrapper אותה חתימת פונקציה כמו ה-poiner ששולח את הקריאה. לאחר מכן, ה-wrapper יכול להפעיל ישירות את קוד ההרכבה. כי הסתעפויות ישירות לא משויכות באמצעות CFI (לא ניתן למנות אותם מחדש בזמן ריצה ולכן הם לא מהווים סיכון אבטחה) הפעולה הזאת תפתור את הבעיה.

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

השבתת CFI

לא תועדה תקורת ביצועים כלשהי, לכן אין צורך להשבית את התכונה CFI. עם זאת, אם קיימת השפעה על המשתמשים, אפשר להשבית את ה-CFI באופן סלקטיבי עבור פונקציות ספציפיות או קובצי מקור מסוימים, על ידי אספקת קובץ רשימה שחורה של כלי חיטוי בזמן הידור. הרשימה השחורה מורה למהדר להשבית את CFI הוספת אינסטרומנטציה במיקומים שצוינו.

מערכת ה-build של Android מספקת תמיכה ברשימות שחורות לפי רכיב (מאפשרת אפשר לבחור קובצי מקור או פונקציות ספציפיות שלא יקבלו CFI אינסטרומנטציה) עבור 'לעשות' ו'סוונג'. לפרטים נוספים על הפורמט של רשימה שחורה, מידע נוסף בנושא upstream מסמכי Clang.

אימות

בשלב זה, אין בדיקות CTS ספציפיות ל-CFI. במקום זאת, עליכם לוודא בדיקות CTS עוברות בהצלחה עם או בלי CFI מופעל, כדי לוודא ש-CFI לא משפיע על במכשיר.