בדיקת תקינות של זרימת הבקרה בליבת המערכת

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

החל מ-Android 9, אפשר להפעיל CFI בליבת המערכת.

ליבת לינוקס כוללת שתי הטמעות שונות של CFI:

  • ב-Linux מגרסה 6.0 ומטה, Clang CFI, שמסתמך על Clang LTO
  • ב-Linux 6.1 ומעלה, Clang KCFI

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

‫Clang KCFI לא דורש LTO, ולכן ליבות Android עדכניות יותר נהנות מ-CFI בלי התקורה של LTO בזמן הבנייה.

הטמעה

האפשרות CONFIG_CFI_CLANG קובעת אם להפעיל את Clang CFI או את Clang KCFI.

פרטים טכניים נוספים על CFI ועל אופן הטיפול בבדיקות אחרות של העברת שליטה זמינים במסמכי התיעוד של LLVM. במאמר הזה, KCFI נקרא -fsanitize=kcfi.

פתרון בעיות

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

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

אימות

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