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

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

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

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

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

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

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

CFI מופעל כברירת מחדל למכשירי Arm64 עבור קבוצת הרכיבים ב- /platform/build/target/product/cfi-common.mk . זה מופעל ישירות גם במערך קבצי makefiles / blueprint של רכיבי מדיה, כגון /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 ברכיבים חדשים על ידי הוספת מספר שורות לקבצי ה- makefiles או התבנית שלך.

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

כדי לאפשר 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 במהלך הבנייה.
  • LOCAL_SANITIZE_DIAG מפעיל מצב אבחון עבור CFI. מצב אבחון מדפיס מידע נוסף בנושא ניפוי באגים ב- logcat במהלך קריסות, דבר שימושי בעת פיתוח ובדיקת המבנים שלך. הקפד להסיר את מצב האבחון במערכות הפקות.
  • 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 בקוד המכיל קריאות עקיפות להרכבה. מכיוון שקוד הרכבה אינו מוקלד, הדבר גורם לאי התאמה בין סוגים.

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

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

השבתת CFI

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

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

מַתַן תוֹקֵף

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