נכון לשנת 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/ /platform/build/target/product/cfi-common.mk
. זה גם מופעל ישירות בקבוצה של קבצי makefile/מתווה של רכיבי מדיה, כגון /platform/frameworks/av/media/libmedia/Android.bp
ו-/platform/frameworks/av/cmds/stagefright/ /platform/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 לדוגמה:
- Bluetooth : /c/platform/system/bt/+/532377
- NFC : /c/platform/system/nfc/+/527858
בעיה אפשרית נוספת היא ניסיון להפעיל CFI בקוד המכיל קריאות עקיפות ל-assembly. מכיוון שקוד ההרכבה אינו מוקלד, זה גורם לאי התאמה של סוג.
כדי לתקן זאת, צור עטיפות קוד מקוריות עבור כל קריאת assembly, ותן לעטיפות את אותה חתימת פונקציה כמו המצביע הקורא. לאחר מכן, העטיפה יכולה לקרוא ישירות לקוד ההרכבה. מכיוון שסניפים ישירים אינם מוגנים על ידי CFI (לא ניתן להצביע מחדש בזמן ריצה ולכן אינם מהווים סיכון אבטחה), זה יפתור את הבעיה.
אם יש יותר מדי פונקציות assembly ולא ניתן לתקן את כולן, ניתן גם רשימה שחורה של כל הפונקציות המכילות קריאות עקיפות ל-assembly. זה לא מומלץ מכיוון שהוא משבית את בדיקות ה-CFI בפונקציות אלו, ובכך פותח את משטח ההתקפה.
השבתת CFI
לא ראינו ביצועים כלשהם, כך שלא תצטרך להשבית את CFI. עם זאת, אם יש השפעה מול המשתמש, אתה יכול להשבית באופן סלקטיבי את CFI עבור פונקציות בודדות או קובצי מקור על ידי אספקת קובץ רשימה שחורה של חיטוי בזמן ההידור. הרשימה השחורה מורה למהדר להשבית את מכשור ה-CFI במיקומים שצוינו.
מערכת הבנייה של אנדרואיד מספקת תמיכה ברשימות שחורות לכל רכיב (המאפשרת לך לבחור קבצי מקור או פונקציות בודדות שלא יקבלו מכשור CFI) עבור Make ו-Soong כאחד. לפרטים נוספים על הפורמט של קובץ רשימה שחורה, עיין במסמכי Clang במעלה הזרם .
מַתַן תוֹקֵף
נכון לעכשיו, אין מבחן CTS במיוחד עבור CFI. במקום זאת, ודא שבדיקות CTS עוברות עם או בלי CFI מופעל כדי לוודא ש-CFI לא משפיע על המכשיר.