UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) לחפש סוגים שונים של התנהגות לא מוגדרת. בעוד ש-UBSan יכול מתבצע זיהוי הרבה באגים בלתי מוגדרים בהתנהגות, Android תומך:

  • יישור
  • בוליאני
  • גבולות
  • טיפוסים בני מנייה (enum)
  • מספר ממשי (float-cast-overflow)
  • חלוקת מספר ממשי (float) באפס
  • מספר שלם חלקי אפס
  • מאפיין שאינו אפס
  • null
  • חזרה לרצף השגרה
  • Return-nonnull-attribute
  • Shift-base
  • Shift-exponent
  • מספר שלם חתום
  • לא נגיש
  • שלם-שלם ללא חתימה
  • קשר ל-Vla

unsigned-integer-overflow, אבל לא מוגדרים מבחינה טכנית כלולה במכשיר החיטוי ומשמשת במודולים רבים של Android, כולל רכיבי שרת המדיה, כדי למנוע גלישת מספרים שלמים נסתרים נקודות חולשה.

הטמעה

במערכת ה-build של Android, אפשר להפעיל את UBSan באופן גלובלי או באופן מקומי. כדי להפעיל UBSan באופן גלובלי, מגדירים SANITIZE_TARGET ב-Android.mk. כדי להפעיל את UBSan לכל מודול, להגדיר LOCAL_SANITIZE ולציין את ההתנהגויות הלא מוגדרות שאתם רוצים לחפש ב-Android.mk. לדוגמה:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

ותצורת התוכנית המקבילה (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

קיצורי דרך ב-UBSan

ב-Android יש גם שני קיצורי דרך, integer ו default-ub, כדי להפעיל קבוצה של חומרי חיטוי בו-זמנית. מספר שלם מפעילה את integer-divide-by-zero, signed-integer-overflow וגם unsigned-integer-overflow. default-ub מאפשר את הבדיקות שיש להן מהדר (compiler) מינימלי בעיות בביצועים: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. ניתן להשתמש במחלקה לחיטוי מספרים שלמים עם SANITIZE_TARGET וגם LOCAL_SANITIZE, ואילו בברירת המחדל ניתן להשתמש רק עם SANITIZE_TARGET.

דיווח טוב יותר על שגיאות

הטמעת UBSan כברירת מחדל ב-Android מפעילה פונקציה מוגדרת כאשר יש התנהגות לא מוגדרת. כברירת מחדל, הפונקציה הזו מבוטלת. אבל, לפעמים החל מאוקטובר 2016, ל-UBSan ב-Android יש ספרייה אופציונלית של סביבת זמן ריצה מספק דיווח מפורט יותר על שגיאות, כולל סוג ההתנהגות הלא מוגדרת. על הקובץ, ועל שורת קוד המקור. כדי להפעיל את השגיאה הזו דיווח באמצעות בדיקות מספרים שלמים מוסיפים את הטקסט הבא לקובץ Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

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

לפניכם דוגמה למידע שסופק על ידי ספריית זמן הריצה של UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

ניקיון נוסף של מספר שלם (Integer)

חריגות לא מכוונות של מספרים שלמים עלולות לגרום לפגיעה בזיכרון או למידע לחשוף נקודות חולשה במשתנים הקשורים לגישות זיכרון או והקצאות זיכרון. כדי להילחם בתופעה הזו, הוספנו UndefinedBehaviorSanitizer (UBSan) אמצעי חיטוי שאינם חתומים ומספרים שלמים חתומים אל מקשה the media framework ב-Android 7.0. ב-Android 9, מורחבת UBSan כדי לכלול יותר רכיבים ותמיכה משופרת במערכת build עבורה.

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

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

הכלי Integer Overflow Sanitization (IntSan) מסופק על ידי המהדר ומוסיף של אינסטרומנטציה לבינארי במהלך זמן הידור כדי לזהות פונקציות אריתמטיות גלישה. היא מופעלת כברירת מחדל ברכיבים שונים לאורך לדוגמה, /platform/external/libnl/Android.bp

הטמעה

IntSan משתמש במכשירי ניקוי נוספים למספרים שלמים חתומים ולא חתומים של UBSan. הזה המיטיגציה מופעלת ברמה לכל מודול. הוא עוזר לשמור על רכיבים קריטיים של Android מאובטחים ואין להשבית אותם.

מומלץ מאוד להפעיל את התכונה 'חיטוי Integer Overflow' כדי לקבל רכיבים. המועמדים האידיאליים הם קוד נייטיב בעל הרשאות או קוד נייטיב מנתחת קלט לא מהימן של משתמשים. יש תקורת ביצועים קטנה שמשויכת בעזרת תכשיר לחיטוי תלוי בשימוש בקוד ובמידת השכיחות של פעולות אריתמטיות. יש לצפות לאחוז התקורה קטן ולבדוק אם בעיה בביצועים.

תמיכה ב-IntSan ב-createfiles

כדי להפעיל את IntSan בקובץ Maker, מוסיפים:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE לוקחת רשימה של תכשירים לחיטוי שמופרדים בפסיקים, כש-integer_overflow היא קבוצת אפשרויות ארוזה מראש מכלי ניקיון לכלים חתומים או לא חתומים ברירת מחדל רשימת חסימה.
  • מצב אבחון מופעל על ידי LOCAL_SANITIZE_DIAG חומרי חיטוי. יש להשתמש במצב האבחון רק במהלך הבדיקה, כי הפעולה הזו לא לבטל חריגות, לשלול לחלוטין את היתרון האבטחה של צמצום ההשפעות. ראו פתרון בעיות .
  • LOCAL_SANITIZE_BLOCKLIST מאפשר לציין רשימת BLOCKLIST כדי למנוע חיטוי של פונקציות וקובצי מקור. צפייה פתרון בעיות בנושאים נוספים פרטים.

אם ברצונך שליטה פרטנית יותר, יש להפעיל את חומרי החיטוי בנפרד באמצעות או את שני הדגלים:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

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

כדי להפעיל 'ניקוי נתונים נוספים' ממספרים שלמים בקובץ תוכנית לניהול, כמו /platform/external/libnl/Android.bp add:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

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

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

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

כדי להפעיל את חומרי החיטוי בנפרד, משתמשים ב:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

פתרון בעיות

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

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

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

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

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

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

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

  • ארגון מחדש של הקוד כדי למנוע עומס יתר (דוגמה)
  • גלישה מפורשת דרך __builtin_*_overflow - של Clang פונקציות (דוגמה)
  • השבתת החיטוי באמצעות הפונקציה על ידי ציון המאפיין no_sanitize (דוגמה)
  • השבתת החיטוי של פונקציה או של קובץ מקור באמצעות קובץ BLOCKLIST (דוגמה)

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

דוגמאות לדפוסים נפוצים שעשויים לגרום לחריגות לא תקינות:

  • משתמע העברות (cast) שבהן מתרחש גלישה לא חתומה לפני ההעברה לסוג חתום (דוגמה)
  • מחיקות של רשימות מקושרות שמפחיתות את אינדקס הלולאה בזמן המחיקה (דוגמה)
  • הקצאת סוג לא חתום ל-1- במקום לציין את הערך המקסימלי בפועל (דוגמה)
  • לולאות שמפחיתות מספר שלם לא חתום בתנאי (דוגמה, דוגמה)

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

השבתת IntSan

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

מידע נוסף על השבתת IntSan זמין במסמכי התיעוד של Clang ב-upstream. עם פונקציה מאפיינים וקובץ BLOCKLIST פורמט. רשימת החסימה צריכה להיות בהיקף לחיטוי ספציפי באמצעות ולהשתמש בשמות קטעים שמציינים את תכשיר לחיטוי היעד כדי למנוע פגיעה חומרי חיטוי.

אימות

בשלב הזה, אין בדיקת CTS ספציפית לחיטוי Integer Overflow. במקום זאת, עליך לוודא שבדיקות CTS עוברות בהצלחה עם או בלי הפעלה של IntSan לצורך אימות שהיא לא משפיעה על המכשיר.

ניקיון של גבולות

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

הטמעה

BoundSan משתמש ב-UBSan כלי לחיטוי גבולות. ההקלות האלה מופעלות ברמה לכל מודול. זה עוזר לשמור על האבטחה של רכיבים קריטיים ב-Android ואין להשבית אותם.

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

הפעלת BoundSan בקובצי עיצוב

כדי להפעיל את BoundSan בקובצי שרטוטים, צריך להוסיף "bounds" למאפיין החיטוי של misc_undefined עבור בינארי וספרייה מודולים:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
אבחון

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

רשימת חסימה

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

הפעלת BoundSan ב-Makefiles

אפשר להפעיל את BoundSan ב-getfiles על ידי הוספת "bounds" למשתנה LOCAL_SANITIZE למודולים בינאריים וספריות:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

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

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

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

השבתת BoundSan

אפשר להשבית את BoundSan בפונקציות ובקובצי מקור באמצעות רשימות BLOCKLIST או במאפייני הפונקציה. עדיף להשאיר את BoundSan במצב מופעל, לכן להשבית אותו רק אם שהפונקציה או הקובץ יוצרים כמות גדולה של תקורת ביצועים המקור נבדק באופן ידני.

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

אימות

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

פתרון בעיות

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

ניתן לזהות בקלות שגיאות BoundSan כי הן כוללות את הפרטים הבאים: הודעת ביטול של Tombstone:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

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

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'