自 2025 年 3 月 27 日起,我們建議您使用 android-latest-release
而非 aosp-main
建構及貢獻 AOSP。詳情請參閱「Android 開放原始碼計畫變更」。
控制流程完整性
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
截至 2016 年,Android 上約有 86% 的漏洞與記憶體安全性有關。攻擊者會利用大多數漏洞,變更應用程式的正常控制流程,以便利用遭到攻擊的應用程式所有權限,執行任意惡意活動。控制流程完整性 (CFI) 是一種安全機制,可禁止對已編譯二進位檔的原始控制流程圖進行變更,因此攻擊者要執行這類攻擊的難度會大幅提高。
在 Android 8.1 中,我們在媒體堆疊中啟用了 LLVM 的 CFI 實作。在 Android 9 中,我們在更多元件和核心中啟用 CFI。系統 CFI 預設為開啟,但您需要啟用核心 CFI。
LLVM 的 CFI 需要使用連結時間最佳化 (LTO) 進行編譯。LTO 會保留物件檔案的 LLVM 位元碼表示法,直到連結時間為止,讓編譯器能更妥善地判斷可執行哪些最佳化作業。啟用 LTO 可縮減最終二進位檔的大小並提升效能,但會增加編譯時間。在 Android 上進行測試時,LTO 和 CFI 的組合會對程式碼大小和效能造成微不足道的額外負擔;在少數情況下,兩者都會有所改善。
如要進一步瞭解 CFI 的技術細節,以及如何處理其他前向控制檢查,請參閱 LLVM 設計文件。
範例和來源
CFI 是由編譯器提供,並在編譯期間將檢測功能新增至二進位檔。我們在 Clang 工具鍊和 AOSP 中的 Android 建構系統中支援 CFI。
根據預設,Arm64 裝置會為 /platform/build/target/product/cfi-common.mk
中的一組元件啟用 CFI。您也可以直接在一系列媒體元件的 Makefile/藍圖檔案中啟用此功能,例如 /platform/frameworks/av/media/libmedia/Android.bp
和 /platform/frameworks/av/cmds/stagefright/Android.mk
。
實作系統 CFI
如果您使用 Clang 和 Android 建構系統,系統會預設啟用 CFI。CFI 可協助保障 Android 使用者的安全,因此不應停用。
事實上,我們強烈建議您為其他元件啟用 CFI。理想的候選項目是具有特殊權限的原生程式碼,或是處理不受信任的使用者輸入內容的原生程式碼。如果您使用的是 clang 和 Android 建構系統,可以在 Makefile 或藍圖檔案中加入幾行程式碼,在新的元件中啟用 CFI。
在 Makefile 中支援 CFI
如要在 /platform/frameworks/av/cmds/stagefright/Android.mk
等建構檔案中啟用 CFI,請新增以下內容:
LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
LOCAL_SANITIZE
會在建構期間將 CFI 指定為消毒劑。
LOCAL_SANITIZE_DIAG
會為 CFI 開啟診斷模式。診斷模式會在應用程式發生當機時,在 Logcat 中列印其他偵錯資訊,這在開發及測試版本時非常實用。不過,請務必在正式版版本中移除診斷模式。
LOCAL_SANITIZE_BLACKLIST
可讓元件有選擇地為個別函式或來源檔案停用 CFI 檢測功能。您可以使用黑名單做為最後手段,解決可能存在的任何使用者問題。詳情請參閱「停用 CFI」。
在藍圖檔案中支援 CFI
如要在藍圖檔案 (例如 /platform/frameworks/av/media/libmedia/Android.bp
) 中啟用 CFI,請新增以下內容:
sanitize: {
cfi: true,
diag: {
cfi: true,
},
blacklist: "cfi_blacklist.txt",
},
疑難排解
如果您在新元件中啟用 CFI,可能會遇到幾個函式類型不相符錯誤和彙整程式碼類型不相符錯誤的問題。
由於 CFI 會限制間接呼叫,只跳至與呼叫中使用的靜態類型相同的動態類型函式,因此會發生函式類型不相符的錯誤。CFI 會限制虛擬和非虛擬成員函式呼叫,只跳到用於呼叫的物件靜態類型的衍生類別物件。也就是說,如果程式碼違反上述任一假設,CFI 所新增的檢測功能就會中止。舉例來說,堆疊追蹤記錄會顯示 SIGABRT,而 Logcat 會包含一行關於控制流程完整性發現不相符的訊息。
如要修正這個問題,請確認呼叫的函式與靜態宣告的類型相同。以下是兩個 CL 範例:
另一個可能的問題是,嘗試在包含對彙編程式間接呼叫的程式碼中啟用 CFI。由於組合語程式碼未經過型別化,因此會導致型別不相符。
如要修正這個問題,請為每個組合呼叫建立原生程式碼包裝函式,並為包裝函式提供與呼叫指標相同的函式簽章。接著,包裝函式就能直接呼叫彙整程式碼。由於直接分支並未由 CFI 檢測 (無法在執行階段重新指派,因此不會造成安全性風險),因此這麼做就能解決問題。
如果組合函式過多,且無法全部修正,您也可以將所有包含對組合間接呼叫的函式加入黑名單。但不建議這麼做,因為這會停用這些函式的 CFI 檢查,進而開放攻擊面。
停用 CFI
我們並未觀察到任何效能額外負擔,因此您不必停用 CFI。不過,如果有使用者面臨的影響,您可以在編譯時提供清理器黑名單檔案,針對個別函式或來源檔案選擇性停用 CFI。黑名單會指示編譯器在指定位置停用 CFI 檢測。
Android 建構系統支援 Make 和 Soong 的個別元件黑名單 (可讓您選擇不會接收 CFI 檢測的來源檔案或個別函式)。如要進一步瞭解黑名單檔案的格式,請參閱 上游 Clang 說明文件。
驗證
目前沒有專門針對 CFI 進行的 CTS 測試。請改為確認啟用或停用 CFI 時,CTS 測試是否都能通過,以驗證 CFI 不會影響裝置。
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-27 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-07-27 (世界標準時間)。"],[],[],null,["# Control flow integrity\n\nAs of 2016, about 86% of all vulnerabilities on Android are memory safety\nrelated. Most vulnerabilities are exploited by attackers changing the normal\ncontrol flow of an app to perform arbitrary malicious activities with\nall the privileges of the exploited app.\n[Control flow\nintegrity (CFI)](https://clang.llvm.org/docs/ControlFlowIntegrity.html) is a security mechanism that disallows changes to the\noriginal control flow graph of a compiled binary, making it significantly harder\nto perform such attacks.\n\n\nIn Android 8.1, we enabled LLVM's implementation of CFI in the media stack. In\nAndroid 9, we enabled CFI in more components and also the kernel. System CFI is\non by default but you need to enable kernel CFI.\n\n\nLLVM's CFI requires compiling with\n[Link-Time Optimization\n(LTO)](https://llvm.org/docs/LinkTimeOptimization.html). LTO preserves the LLVM bitcode representation of object files until\nlink-time, which allows the compiler to better reason about what optimizations\ncan be performed. Enabling LTO reduces the size of the final binary and improves\nperformance, but increases compile time. In testing on Android, the combination\nof LTO and CFI results in negligible overhead to code size and performance; in a\nfew cases both improved.\n\n\nFor more technical details about CFI and how other forward-control checks are\nhandled, see the [LLVM design\ndocumentation](https://clang.llvm.org/docs/ControlFlowIntegrityDesign.html).\n\nExamples and source\n-------------------\n\n\nCFI is provided by the compiler and adds instrumentation into the binary during\ncompile time. We support CFI in the Clang toolchain and the Android build system\nin AOSP.\n\n\nCFI is enabled by default for Arm64 devices for the set of components in\n[/platform/build/target/product/cfi-common.mk](https://android.googlesource.com/platform/build/+/android16-release/target/product/cfi-common.mk).\nIt's also directly enabled in a set of media components' makefiles/blueprint\nfiles, such as [/platform/frameworks/av/media/libmedia/Android.bp](https://android.googlesource.com/platform/frameworks/av/+/android16-release/media/libmedia/Android.bp#117)\nand [/platform/frameworks/av/cmds/stagefright/Android.mk](https://android.googlesource.com/platform/frameworks/av/+/android16-release/cmds/stagefright/Android.mk#188).\n\nImplementing system CFI\n-----------------------\n\n\nCFI is enabled by default if you use Clang and the Android build system.\nBecause CFI helps keep Android users safe, you should not disable it.\n\n\nIn fact, we strongly encourage you to enable CFI for additional components.\nIdeal candidates are privileged native code, or native code that processes\nuntrusted user input. If you're using clang and the Android build system, you\ncan enable CFI in new components by adding a few lines to your makefiles or\nblueprint files.\n\n### Supporting CFI in makefiles\n\n\nTo enable CFI in a make file, such as [/platform/frameworks/av/cmds/stagefright/Android.mk](https://android.googlesource.com/platform/frameworks/av/+/android16-release/cmds/stagefright/Android.mk#188),\nadd:\n\n\n```maple\nLOCAL_SANITIZE := cfi\n# Optional features\nLOCAL_SANITIZE_DIAG := cfi\nLOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt\n```\n\n- `LOCAL_SANITIZE` specifies CFI as the sanitizer during the build.\n- `LOCAL_SANITIZE_DIAG` turns on diagnostic mode for CFI. Diagnostic mode prints out additional debug information in logcat during crashes, which is useful while developing and testing your builds. Make sure to remove diagnostic mode on productions builds, though.\n- `LOCAL_SANITIZE_BLACKLIST` allows components to selectively disable CFI instrumentation for individual functions or source files. You can use a blacklist as a last resort to fix any user-facing issues that might otherwise exist. For more details, see [Disabling CFI](#disabling-cfi).\n\n### Supporting CFI in blueprint files\n\n\nTo enable CFI in a blueprint file, such as [/platform/frameworks/av/media/libmedia/Android.bp](https://android.googlesource.com/platform/frameworks/av/+/android16-release/media/libmedia/Android.bp#117),\nadd: \n\n```scdoc\n sanitize: {\n cfi: true,\n diag: {\n cfi: true,\n },\n blacklist: \"cfi_blacklist.txt\",\n },\n```\n\n### Troubleshooting\n\n\nIf you're enabling CFI in new components, you may run into a few issues with\n*function type mismatch errors* and *assembly code type mismatch\nerrors*.\n\n\nFunction type mismatch errors occur because CFI restricts indirect calls to only\njump to functions that have the same dynamic type as the static type used in the\ncall. CFI restricts virtual and non-virtual member function calls to only jump\nto objects that are a derived class of the static type of the object used to\nmake the call. This means, when you have code that violates either of these\nassumptions, the instrumentation that CFI adds will abort. For example, the\nstack trace shows a SIGABRT and logcat contains a line about control flow\nintegrity finding a mismatch.\n\n\nTo fix this, ensure that the called function has the same type that was\nstatically declared. Here are two example CLs:\n\n- **Bluetooth** : [/c/platform/system/bt/+/532377](https://android-review.googlesource.com/c/platform/system/bt/+/532377)\n- **NFC** : [/c/platform/system/nfc/+/527858](https://android-review.googlesource.com/c/platform/system/nfc/+/527858)\n\n\nAnother possible issue is trying to enable CFI in code that contains indirect\ncalls to assembly. Because assembly code is not typed, this results in a type\nmismatch.\n\n\nTo fix this, create native code wrappers for each assembly call, and give the\nwrappers the same function signature as the calling poiner. The wrapper can then\ndirectly call the assembly code. Because direct branches are not instrumented by\nCFI (they cannot be repointed at runtime and so do not pose a security risk),\nthis will fix the issue.\n\n\nIf there are too many assembly functions and they cannot all be fixed, you can\nalso blacklist all functions that contain indirect calls to assembly. This is\nnot recommended as it disables CFI checks on these functions, thereby opening\nattack surface.\n\n### Disabling CFI\n\n\nWe didn't observe any performance overhead, so you shouldn't need to disable\nCFI. However, if there is a user-facing impact, you can selectively disable CFI\nfor individual functions or source files by supplying a sanitizer blacklist file\nat compile time. The blacklist instructs the compiler to disable CFI\ninstrumentation in specified locations.\n\n\nThe Android build system provides support for per-component blacklists (allowing\nyou to choose source files or individual functions that will not receive CFI\ninstrumentation) for both Make and Soong. For more details on the format of a\nblacklist file, see the [upstream\nClang docs](https://clang.llvm.org/docs/ControlFlowIntegrity.html#blacklist).\n\nValidation\n----------\n\n\nCurrently, there are no CTS test specifically for CFI. Instead, make sure that\nCTS tests pass with or without CFI enabled to verify that CFI isn't impacting\nthe device."]]