整數溢出清理

意外的整數溢出可能導致與內存訪問或內存分配相關的變量中的內存損壞或信息洩露漏洞。為了解決這個問題,我們添加了 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_BLACKLIST := modulename_blacklist.txt
  • LOCAL_SANITIZE採用逗號分隔的清理程序列表, integer_overflow是帶有默認黑名單的單個有符號和無符號整數溢出清理程序的預打包選項集。
  • LOCAL_SANITIZE_DIAG打開消毒劑的診斷模式。僅在測試期間使用診斷模式,因為這不會在溢出時中止,完全否定緩解的安全優勢。有關其他詳細信息,請參閱故障排除
  • LOCAL_SANITIZE_BLACKLIST允許您指定黑名單文件以防止函數和源文件被清理。有關其他詳細信息,請參閱故障排除

如果您想要更精細的控制,請使用一個或兩個標誌單獨啟用消毒劑:

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,
      },
      blacklist: "modulename_blacklist.txt",
   },

與 make 文件一樣, integer_overflow屬性是一組預打包的選項,用於具有默認黑名單的單個有符號和無符號整數溢出清理器。

diag屬性集為消毒劑啟用診斷模式。僅在測試期間使用診斷模式。診斷模式不會在溢出時中止,這完全否定了用戶構建中緩解的安全優勢。有關其他詳細信息,請參閱故障排除

blacklist屬性允許指定允許開發人員防止函數和源文件被清理的黑名單文件。有關其他詳細信息,請參閱故障排除

要單獨啟用消毒劑,請使用:

   sanitize: {
      misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
      diag: {
          misc_undefined: ["signed-integer-overflow",
                           "unsigned-integer-overflow",],
      },
      blacklist: "modulename_blacklist.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屬性禁用函數中的清理(示例
  • 通過黑名單文件禁用函數或源文件的清理(示例

您應該盡可能使用最細粒度的解決方案。例如,具有許多算術運算和單個溢出運算的大型函數應該重構單個運算,而不是將整個函數列入黑名單。

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

  • 在轉換為有符號類型之前發生無符號溢出的隱式轉換示例
  • 刪除時減少循環索引的鍊錶刪除(示例
  • 將無符號類型分配給 -1 而不是指定實際的最大值( 示例
  • 在條件中減少無符號整數的循環( 示例示例

建議開發人員在禁用清理之前確保清理程序檢測到溢出的情況確實是良性的,沒有意外的副作用或安全隱患。

禁用 IntSan

您可以使用黑名單或函數屬性禁用 IntSan。只有在重構代碼不合理或存在有問題的性能開銷時才謹慎禁用。

有關使用函數屬性黑名單文件格式禁用 IntSan 的更多信息,請參閱上游 Clang 文檔。通過使用指定目標消毒劑的部分名稱,應將黑名單限制在特定消毒劑的範圍內,以避免影響其他消毒劑。

驗證

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