自 2025 年 3 月 27 日起,我们建议您使用 android-latest-release
而非 aosp-main
构建 AOSP 并为其做出贡献。如需了解详情,请参阅 AOSP 的变更。
控制流完整性
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
从 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 工具链中的 CFI 和 AOSP 中的 Android 构建系统。
对于 Arm64 设备上位于 /platform/build/target/product/cfi-common.mk
的一组组件,CFI 默认处于启用状态。在一组媒体组件的 makefile/blueprint 文件(如 /platform/frameworks/av/media/libmedia/Android.bp
和 /platform/frameworks/av/cmds/stagefright/Android.mk
)中,也直接启用了 CFI。
实现系统 CFI
如果您使用 Clang 和 Android 构建系统,CFI 默认处于启用状态。因为 CFI 有助于确保 Android 用户的安全,所以不应将其停用。
事实上,我们强烈建议您为更多组件启用 CFI。理想的候选对象是特权原生代码,或可处理不可信用户输入的原生代码。如果您使用 Clang 和 Android 构建系统,则可以通过向 makefile 或 blueprint 文件中添加几行代码,在新组件中启用 CFI。
在 makefile 中启用 CFI
如需在 makefile(如 /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 中输出额外的调试信息,这在开发和测试 build 时很有用。不过,请务必对正式版 build 移除诊断模式。
LOCAL_SANITIZE_BLACKLIST
支持组件针对个别函数或源代码文件选择性地停用 CFI 插桩。在万不得已时,您可以使用黑名单来修复用户会遇到的可能以其他方式存在的任何问题。如需了解详情,请参阅停用 CFI。
在 blueprint 文件中启用 CFI
如需在 blueprint 文件(如 /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 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-03-26。
[[["易于理解","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"]],["最后更新时间 (UTC):2025-03-26。"],[],[],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."]]