KCFI

这个 Pass 就一百多行,含义就是:在内核代码的间接调用前插入控制流完整性检查(Control-Flow Integrity, CFI)指令,以提高安全性。
实现 Kernel Control-Flow Integrity (KCFI) 的功能 —— 在执行间接函数调用(如通过函数指针调用)之前插入一个检查指令,验证被调用目标是否匹配预期的类型。

背景

控制流完整性是一种编译器和运行时技术,旨在防止攻击者通过劫持程序的控制流(比如通过函数指针、虚函数表、返回地址等)来执行恶意代码。
攻击者通常利用内存漏洞(如缓冲区溢出、Use-after-free 等)修改代码中用于间接跳转的数据(如函数指针),从而劫持控制流,达到执行任意代码的目的。
KCFI(Kernel Control-Flow Integrity) 是 LLVM 项目为 Linux 内核引入的一种轻量级、可部署的控制流完整性机制。它专注于防护 内核中的间接函数调用,如:

void (*handler)(void) = some_func;
handler(); // 间接调用

攻击者若能修改 handler 指针,便可跳转到非法地址,执行恶意代码。

🧠 KCFI 的工作原理

KCFI 的核心思想是:

类型标签(CFI Type)
  • 编译器在编译函数时,为每个函数分配一个类型标签(type hash),用于标识该函数的“调用签名”。
  • 任何使用函数指针调用的地方,都会带上一个期望的 cfi-type 标签。
插入检查
  • 编译器生成的机器代码会在每次间接调用前,插入一条指令来验证目标地址的标签是否匹配期望类型
  • 如果标签不匹配 → 报错 / 崩溃,防止攻击者调用非法代码。
运行时无需复杂支持
  • 标签是通过专门的指令嵌入在函数开头或某个固定位置,运行时只需读取目标地址处的数据进行比较。
  • 无需复杂的运行时类型系统,代价非常小,适合内核这种对性能要求极高的环境。
对比
特性 传统 CFI KCFI
应用范围 用户态/内核态 专注于 Linux 内核
实现方式 依赖运行时检查 静态编译器插入检查,轻量
开销 相对较高 极低
灵活性 支持多种语言/签名识别 仅支持固定签名(函数指针类型)
安全性粒度 更细粒度(可按签名匹配) 通过类型 hash 匹配