✨ 要点🔬 技术摘要
想象一下,操作系统(你电脑的“大脑”)有一个非常严格的安全守卫。这个守卫被称为 eBPF 验证器(eBPF Verifier) 。它会在任何小程序(比如交通警察或安全扫描仪)进入内核运行之前进行检查。这个守卫极其谨慎:它只允许使用一种非常简单、安全的语言编写的程序运行。
当守卫说“好吧,这是安全的”之后,一个翻译器(JIT 编译器 )会将这种简单的语言转换成计算机的母语,以便快速运行。
问题:“一次一个词”的翻译器
本文解释了目前的翻译器被设计得非常简单且值得信赖。它一次只翻译一条指令 ,采用单向传递的方式。它不会向前瞻看,也不会试图变得聪明。它不会预判,只是机械地执行。
想象一下,这就像是一个被禁止使用成语或捷径的翻译官。如果你想表达“旋转一个数字”,这种安全的语言里并没有这样一个专门的词。因此,翻译官必须用十个不同的词写出一句冗长、笨拙的句子来解释同一件事。
结果: 计算机必须阅读并执行一段冗长、混乱的句子,而不是使用单个强大的命令。这使得 eBPF 程序的运行速度比直接用母语编写时慢了多达两倍 。
解决方案:Kops(“神奇护照”)
作者创建了一个名为 Kops 的系统。把 Kops 想象成一个特殊的“神奇护照”系统,它允许计算机在不违反安全守卫规则的情况下,使用强大的原生快捷方式。
它是这样运作的,我们使用一个简单的类比:
两部分组成的护照: 每一种新的“快捷方式”(比如硬件旋转或条件选择)都带有两个部分:
证明(安全版本): 用安全的语言编写的一段冗长、枯燥、循序渐进的解释。安全守卫会检查这段内容以确保其安全性。
原生发射(神奇版本): 计算机硬件实际执行的一个单一、强大的指令。
流程:
在程序运行之前,一个“识别器”(一个在内核之外的智能工具)会寻找模式。如果它看到一段符合已知快捷方式的冗长、笨拙的句子,它就会将其替换为“神奇护照”。
安全守卫 仍然看到的是那段冗长、枯燥的“证明”版本。它检查这段内容,确认其“安全”,然后给出绿灯。
翻译器 随后看到了“神奇护照”。它不再翻译那句冗长的句子,而是直接将其替换为单个、强大的原生指令。
安全保证: 安全守卫永远看不到“神奇”版本。它只信任“证明”。系统中唯一需要信任的新东西,就是将“证明”转化为“神奇”指令的那段特定代码。作者使用一个名为 Lean 4 的工具进行了数学证明,证明了“神奇”指令所做的事情与长篇“证明”句子所做的事情完全一致 。
他们构建了什么?(EInsn)
利用 Kops,他们构建了一组被称为 EInsn 的七个特定快捷方式。这些是计算机非常擅长通过一步完成的操作(如位旋转或数值选择),但原有的安全语言通常会强迫将其拆分为多个步骤。
结果
速度: 通过使用这些快捷方式,他们在某些计算机上使 eBPF 程序运行速度提升了 24% ,在另一些计算机上提升了 22% 。在现实世界的应用中(如网络流量管理),他们看到了高达 12% 的速度提升 。
安全性: 他们无需更改安全守卫的规则,也无需让翻译器变得更复杂。“受信任”的部分依然保持着极小的规模和高度的安全。
灵活性: 如果一台计算机不支持某个特定的快捷方式,系统只需退回到长篇、安全的版本即可。它不会导致崩溃。
大局观
想象一下你正在驾驶一辆汽车(计算机)。现有的规则规定你必须以 20 英里/小时的速度行驶,因为车速表坏了,只能以 5 英里/小时为增量进行读取。即使路况良好,你也无法开得更快。
Kops 就像是一张特殊的许可证,上面写着:“我们知道你正在安全驾驶(证明),所以我们会允许你使用汽车实际的车速表(原生发射)来开得更快,但仅限于特定的、预先批准的路段。”
他们证明了这种新的驾驶方式与旧的方式一样安全,但能让你更快地到达目的地。
技术摘要:Kops —— 安全地扩展 eBPF 编译流水线以支持原生操作
1. 问题陈述
eBPF(扩展伯克利数据包过滤器)通过采用编译流水线来安全地扩展操作系统内核,用于网络、可观测性和安全性领域。在该流水线中,内核内验证器会在即时编译(JIT)将字节码翻译为原生代码之前,检查程序是否安全。然而,内核 JIT 的设计初衷是简单且值得信赖的,它采用单次遍历的方式逐条翻译字节码指令。这种“逐指令码”(per-opcode)的设计设计限制了跨指令优化,并且无法识别现代 CPU 可以作为单条指令执行的硬件惯用法(例如:旋转、条件选择)。
论文中的特征分析显示,与原生编译的代码相比,eBPF 程序在 ARM64 上运行速度慢达 1.98 倍 ,在 x86-64 上慢达 1.57 倍 。性能差距主要是由内核 JIT 无法将多条指令序列优化为高效的原生模式所驱动的。现有解决方案受到限制:由于内核发布周期长、需要上游接受以及需要扩大可信计算基(TCB),直接在内核 JIT 中添加优化非常困难。用户态优化器(如 Merlin、K2)可以重写字节码,但无法在标准 eBPF 指令集之外发射原生指令。
2. 方法论:Kops 机制
作者提出了 Kops ,一种扩展接口,允许用户态编译器和内核模块在不修改内核核心的情况下引入新操作。Kops 基于每个新操作的双重表示 原则进行工作:
证明序列(Proof Sequence): 一组 vanilla eBPF 指令序列,由现有的内核内验证器进行检查。这确保了安全性保证(内存安全、终止性)保持不变。
原生发射(Native Emit): 由 JIT 编译器直接发射机器指令。
工作流:
识别(Recognition): 编译时识别器(在用户态)识别出对应于支持操作的模式(例如,特定的一组移位和或运算序列对应于一个旋转操作),并将它们重写为“扩展指令”。
验证(Verification): 在加载时,验证器将扩展指令降级回其证明序列(vanilla eBPF),并执行其标准的安全性分析。
恢复与执行(Restoration & Execution): 如果验证通过,JIT 会恢复该扩展指令,并将其分发给一个提供特定架构原生机器代码的描述符。
约束与安全性:
内核核心的变化是极小且通用的。
验证器保持与架构无关;它只看到证明序列。
可信计算基(TCB) 仅通过每个操作特定的原生发射代码进行扩展。识别器和证明序列不属于 TCB;如果识别器错误地产生了不安全的重写,验证器将会拒绝它。
形式化验证: 作者使用 Lean 4 来证明对于每一个操作,原生发射产生的与证明序列在 eBPF 可见的架构状态是相同的。
3. 核心贡献
Kops 机制: 一个框架,用于在复用现有验证器进行安全检查的同时,通过扩展编译流水线来引入新操作。它将安全性证明(vanilla eBPF)与性能优化(原生发射)分离。
EInsn 实现: Kops 的一个具体实例化,提供了 七种硬件惯用法操作 (旋转 Rotate、条件选择 Conditional Select、位提取 Bit Extract、字节交换 Byte Swap、预取 Prefetch、批量拷贝 Bulk Copy 和 LEA)。这些操作在 x86-64 和 ARM64 上分别映射为单条原生指令,而标准的 eBPF 集合则需要多条指令。
形式化证明: 使用 Lean 4 进行的证明,验证了每个 EInsn 操作的证明序列与原生发射之间的语义等价性,确保验证器的保证能够延续到优化后的代码中。
内核实现: 对 Linux 内核核心进行了 929 行的增量修改(涵盖验证器降级/恢复、JIT 分发和注册)以及一个用户态编译时识别器。
4. 结果
评估是在 x86-64 和 ARM64 上使用 27 个纯字节码微基准测试和生产级应用(Cilium 和 Katran)进行的。
微基准测试性能:
x86-64: EInsn 相比未经修改的内核 JIT 实现了高达 24% 的加速 (1.242×),弥补了 42% 的原生代码性能差距。
ARM64: EInsn 实现了高达 22% 的加速 (1.222×)。
代码大小: 原生代码大小减少了 12–23% 。
生产应用:
Cilium (x86-64): 吞吐量提升了 7.4% (1.074×)。
Katran (ARM64): 吞吐量提升了 7.3% (1.073×)。
开销: 加载时间开销可以忽略不计(相对于标准内核的几何平均比率为 0.99×)。
策略敏感性: 研究强调,盲目启用所有匹配点并不总是能获得最优性能;一种“盈利性”策略(根据特定工作负载的收益来选择指令族)比最大化覆盖范围更有效。
上限比较: 作者将 EInsn 与“内核内原生”(信任整个应用程序的原生代码)方法进行了比较。虽然原生方法在 Cilium 上实现了 2.358× 的加速,但它需要信任应用程序的原生代码,从而显著扩大了 TCB。EInsn 处于中间地带,在不信任完整应用程序二进制文件的情况下,提供了受控的加速。
5. 意义与主张
论文声称,Kops 成功解决了 eBPF JIT 的性能局限性,同时没有破坏其安全性模型或显著扩大其 TCB。通过将安全性证明(vanilla eBPF)与性能优化(原生发射)解耦,Kops 允许引入此前无法在内核中安全利用的硬件特定惯用法。
作者强调,这种方法避免了对上游内核 JIT 进行修改或指令集修订的需求。相反,新的操作作为可加载内核模块进行交付。这项工作证明,通过一小组受限的硬件惯用法,可以回收很大一部分 eBPF-原生性能差距,从而使 eBPF 在保持内核执行所需的严格安全性保证的同时,成为高性能、低延迟内核任务的更可行选择。
每周获取最佳 computer science 论文。
受到斯坦福、剑桥和法国科学院研究人员的信赖。
请查收邮箱确认订阅。
出了点问题,再试一次?
无垃圾邮件,随时退订。