UndefinedBehavior Sanitizer

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

  • יישור
  • bool
  • גבולות
  • enum
  • לצוף-יצוק-הצפה
  • לצוף-לחלק-באפס
  • מספר שלם-חלק-באפס
  • nonnull-attribute
  • ריק
  • לַחֲזוֹר
  • מחזיר-nonnull-attribute
  • בסיס משמרת
  • shift-exponent
  • חתום-מספר שלם-הצפה
  • בלתי ניתן להשגה
  • גלישת מספר שלם ללא חתימה
  • vla-bound

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

יישום

במערכת הבנייה של אנדרואיד, אתה יכול להפעיל את 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

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

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

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

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

הערך LOCAL_SANITIZE מאפשר את החיטוי במהלך הבנייה. 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')

חיטוי של גלישה שלמה

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

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

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

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

יישום

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

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

תמיכה ב-IntSan ב-makefiles

כדי להפעיל IntSan בקובץ makefile, הוסף:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE משתמשת ברשימה מופרדת בפסיקים של חומרי חיטוי, כאשר integer_overflow הוא קבוצה ארוזה מראש של אפשרויות עבור חומרי הניקוי החתומים והלא חתומים בודדים עם ברירת המחדל של BLOCKLIST .
  • 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 , הוסף:

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

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

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

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

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

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

פתרון תקלות

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

כדי למצוא, הפסקות שנגרמו על ידי חיטוי ב-builds של משתמשים, חפש קריסות 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 ( דוגמה )

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

דפוסים נפוצים שעלולים לגרום להצפה שפירה כוללים:

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

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

השבתת IntSan

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

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

מַתַן תוֹקֵף

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

חיטוי גבולות

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

יישום

BoundSan משתמש ב-Bounds חיטוי של UBSan . הפחתה זו מופעלת ברמת כל מודול. זה עוזר לשמור על רכיבים קריטיים של אנדרואיד מאובטחים ואסור להשבית אותם.

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

הפעלת BoundSan בקבצי שרטוט

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

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

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

רשימת חסומים

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

הפעלת BoundSan ב-makefiles

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

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

LOCAL_SANITIZE מקבל רשימה של חומרי חיטוי מופרדים בפסיק.

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

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

השבתת BoundSan

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

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

מַתַן תוֹקֵף

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

פתרון תקלות

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

ניתן לזהות בקלות שגיאות BoundSan מכיוון שהן כוללות את הודעת הפסקת המצבה הבאה:

    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]'