UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) 執行編譯時偵測來檢查各種類型的未定義行為。雖然 UBSan 能夠偵測許多未定義的行為錯誤,但 Android 支援:

  • 結盟
  • 布林值
  • 界線
  • 列舉
  • 浮動鑄造溢出
  • 浮點數除以零
  • 整數除以零
  • 非空屬性
  • 無效的
  • 返回
  • 傳回非空屬性
  • 移位基數
  • 移位指數
  • 有符號整數溢出
  • 無法到達的
  • 無符號整數溢出
  • vla結合

unsigned-integer-overflow 雖然在技術上不是未定義的行為,但已包含在清理程式中並在許多 Android 模組(包括媒體伺服器元件)中使用,以消除任何潛在的整數溢位漏洞。

執行

在Android建置系統中,您可以全域或本機啟用UBSan。若要全域啟用 UBSan,請在 Android.mk 中設定 SANITIZE_TARGET。若要在每個模組層級啟用 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 還有兩個快捷鍵, integerdefault-ub ,可以同時啟用一組清理程式。整數啟用integer-divide-by-zerosigned-integer-overflowunsigned-integer-overflowdefault-ub啟用具有最小編譯器效能問題的檢查: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound 。整數 sanitizer 類別可以與 SANITIZE_TARGET 和 LOCAL_SANITIZE 一起使用,而 default-ub 只能與 SANITIZE_TARGET 一起使用。

更好的錯誤報告

當遇到未定義的行為時,Android 的預設 UBSan 實作會呼叫指定的函數。預設情況下,函數被中止。然而,從 2016 年 10 月開始,Android 上的 UBSan 有一個可選的運行時庫,可以提供更詳細的錯誤報告,包括遇到的未定義行為的類型、文件和源代碼行資訊。若要透過整數檢查啟用此錯誤報告,請將以下內容新增至 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')

整數溢位清理

意外的整數溢位可能會導致記憶體損壞或與記憶體存取或記憶體分配相關的變數中資訊外洩的漏洞。為了解決這個問題,我們添加了 Clang 的UndefinedBehaviorSanitizer (UBSan) 有符號和無符號整數溢位清理程序,以強化 Android 7.0 中的媒體框架。在 Android 9 中,我們擴展了 UBSan 以涵蓋更多元件並改進了對其的建置系統支援。

這樣做的目的是在算術運算/指令(可能會溢出)周圍添加檢查,以便在發生溢出時安全地中止進程。這些清理程序可以緩解整類記憶體損壞和資訊外洩漏洞,其根本原因是整數溢出,例如原始的 Stagefright 漏洞。

範例和來源

整數溢位清理 (IntSan) 由編譯器提供,並在編譯時將偵測新增至二進位檔案以偵測算術溢位。預設情況下,它在整個平台的各個元件中啟用,例如/platform/external/libnl/Android.bp

執行

IntSan 使用 UBSan 的有符號和無符號整數溢位清理程式。此緩解措施是在每個模組層級啟用的。它有助於確保 Android 關鍵元件的安全,不應被停用。

我們強烈建議您為其他元件啟用整數溢位清理。理想的候選者是特權本機程式碼或解析不受信任的使用者輸入的本機程式碼。與清理程序相關的效能開銷很小,這取決於程式碼的使用和算術運算的普遍性。預計開銷百分比較小,並測試效能是否為問題。

在 makefile 中支援 IntSan

若要在 makefile 中啟用 IntSan,請新增:

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",
       },

故障排除

如果您在新元件中啟用整數溢位清理,或依賴已進行整數溢位清理的平台庫,則可能會遇到一些良性整數溢位導致中止的問題。您應該在啟用清理功能的情況下測試組件,以確保良性溢出能夠浮出水面。

若要尋找由使用者建置中的清理導致的中止,請搜尋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')

一旦找到有問題的算術運算,請確保溢出是良性的且有意的(例如,沒有安全隱患)。您可以透過以下方式解決消毒劑中止問題:

  • 重構程式碼以避免溢出(範例
  • 透過 Clang 的__builtin_*_overflow函數明確溢出(範例
  • 透過指定no_sanitize屬性來停用函數中的清理(範例
  • 透過 BLOCKLIST 檔案停用函數或來源檔案的清理(範例

您應該使用盡可能最細粒度的解決方案。例如,具有許多算術運算和單一溢出操作的大型函數應該重構單一操作,而不是對整個函數進行 BLOCKLIST。

可能導致良性溢出的常見模式包括:

建議開發人員在禁用消毒之前確保消毒程序檢測到溢出的情況確實是良性的,不會產生意外的副作用或安全隱患。

禁用 IntSan

您可以使用 BLOCKLIST 或函數屬性來停用 IntSan。僅當重構程式碼不合理或有效能開銷問題時才應謹慎停用。

有關透過函數屬性BLOCKLIST 檔案格式停用 IntSan 的更多信息,請參閱上游 Clang 文件。應使用指定目標消毒劑的節名稱將 BLOCKLISTing 的範圍限定為特定消毒劑,以避免影響其他消毒劑。

驗證

目前,還沒有專門針對整數溢位清理的 CTS 測試。相反,請確保 CTS 測試在啟用或未啟用 IntSan 的情況下通過,以驗證它不會影響裝置。

邊界消毒

BoundsSanitizer (BoundSan) 為二進位檔案新增偵測,以在陣列存取周圍插入邊界檢查。如果編譯器無法在編譯時證明存取是安全的,並且在執行時間是否知道陣列的大小,則會新增這些檢查,以便可以對其進行檢查。 Android 10 在藍牙和編解碼器中部署了 BoundSan。 BoundSan 由編譯器提供,並在整個平台的各個元件中預設為啟用。

執行

BoundSan 使用UBSan 的邊界消毒劑。此緩解措施是在每個模組層級啟用的。它有助於確保 Android 關鍵元件的安全,不應被停用。

我們強烈建議您為其他元件啟用 BoundSan。理想的候選者是特權本機程式碼或解析不受信任的使用者輸入的複雜本機程式碼。與啟用 BoundSan 相關的效能開銷取決於無法證明安全的陣列存取數量。預計平均開銷百分比較小,並測試效能是否為問題。

在藍圖檔中啟用 BoundSan

可以透過將"bounds"新增至二進位和庫模組的misc_undefined sanitize 屬性來在藍圖檔中啟用BoundSan:

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

diag屬性啟用消毒劑的診斷模式。僅在測試期間使用診斷模式。診斷模式不會在溢出時中止,這會抵消緩解措施的安全優勢並帶來更高的效能開銷,因此不建議用於生產建置。

黑名單

BLOCKLIST屬性允許指定 BLOCKLIST 文件,開發人員可以使用該文件來防止函數和原始文件被清理。僅當效能是一個問題並且目標檔案/函數貢獻很大時才使用此屬性。手動審核這些文件/函數以確保數組存取安全。有關更多詳細信息,請參閱故障排除

在 makefile 中啟用 BoundSan

透過在二進位和庫模組的LOCAL_SANITIZE變數新增"bounds"可以在 makefile 中啟用 BoundSan:

    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

您可以在具有 BLOCKLIST 或函數屬性的函數和原始檔案中停用 BoundSan。最好保持 BoundSan 處於啟用狀態,因此僅當函數或檔案產生大量效能開銷並且來源已手動審核時才停用它。

有關透過函數屬性BLOCKLIST 檔案格式停用 BoundSan 的更多信息,請參閱 Clang LLVM文件。透過使用指定目標消毒劑的節名稱將阻止清單的範圍限定為特定消毒劑,以避免影響其他消毒劑。

驗證

沒有專門針對 BoundSan 的 CTS 測試。相反,請確保 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]'