שליטה בשלמות הזרימה

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

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

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

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

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

CFI מסופק על ידי המהדר ומוסיף מכשור לבינארי במהלך זמן ההידור. אנו תומכים ב-CFI בשרשרת הכלים של Clang ובמערכת הבנייה של אנדרואיד ב-AOSP.

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

הטמעת מערכת CFI

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

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

תמיכה ב-CFI בקבצי makefile

כדי להפעיל CFI בקובץ make, כגון /platform/frameworks/av/cmds/stagefright/Android.mk , הוסף:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE מציין את CFI כחומר החיטוי במהלך הבנייה.
  • LOCAL_SANITIZE_DIAG מפעיל מצב אבחון עבור CFI. מצב אבחון מדפיס מידע נוסף על ניפוי באגים ב-logcat במהלך קריסות, וזה שימושי בעת פיתוח ובדיקת ה-builds שלך. עם זאת, הקפד להסיר את מצב האבחון בבניית הפקות.
  • LOCAL_SANITIZE_BLACKLIST מאפשר לרכיבים להשבית באופן סלקטיבי מכשור CFI עבור פונקציות בודדות או קבצי מקור. אתה יכול להשתמש ברשימה שחורה כמוצא אחרון כדי לתקן בעיות מול משתמש שעלולות להתקיים אחרת. לפרטים נוספים, ראה השבתת CFI .

תמיכה ב-CFI בקבצי שרטוט

כדי להפעיל CFI בקובץ שרטוט, כגון /platform/frameworks/av/media/libmedia/Android.bp , הוסף:

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

פתרון תקלות

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

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

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

בעיה אפשרית נוספת היא ניסיון להפעיל CFI בקוד המכיל קריאות עקיפות ל-assembly. מכיוון שקוד ההרכבה אינו מוקלד, זה גורם לאי התאמה של סוג.

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

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

השבתת CFI

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

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

מַתַן תוֹקֵף

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