内核控制流完整性

控制流完整性 (CFI) 是一种安全机制,它不允许更改已编译二进制文件的原始控制流图,因而执行此类攻击变得异常困难。

在 Android 9 中,我们在更多组件以及内核中启用了 LLVM 的 CFI 实现。系统 CFI 默认处于启用状态,但内核 CFI 需要您手动启用。

LLVM 的 CFI 需要使用链接时优化 (LTO) 进行编译。LTO 会一直保留对象文件的 LLVM 位码表示法直至链接时,以便编译器更好地推断可以执行哪些优化。启用 LTO 可缩减最终二进制文件的大小并提高性能,但会增加编译时间。在 Android 上进行测试时,结合使用 LTO 和 CFI 对代码大小和性能开销的影响微乎其微;在少数情况下,这两者都会有所改善。

如需了解有关 CFI 以及如何处理其他前向控制检查的更多技术详情,请参阅 LLVM 设计文档

实现

所有受支持的 Android 内核版本中都包含 kCFI 补丁。CONFIG_CFI_CLANG 选项会启用 kCFI,并在 GKI 中有默认设置。

问题排查

启用 kCFI 后,修正其驱动程序可能存在的任何类型不匹配错误。通过不兼容的函数指针间接调用函数将导致 CFI 故障。当检测到 CFI 故障时,内核会输出一条警告,其中包括被调用的函数和导致故障的堆栈轨迹。您可以通过确保函数指针始终与调用的函数属于同一类型来修正此问题。

如需协助调试 CFI 故障,请启用 CONFIG_CFI_PERMISSIVE,它会输出警告(而不会导致内核崩溃)。切勿在正式版中使用宽容模式。

验证

目前,没有专门针对 CFI 的 CTS 测试。不过,您可以确保无论是否启用 CFI,均能通过 CTS 测试,从而确认 CFI 不会影响设备。