BPF
起源
BPF 的全称是 Berkeley Packet Filter, 即伯克利报文过滤器,它的设计思想来源于 1992 年的一篇论文The BSD packet filter: A New architecture for user-level packet capture(《BSD数据包过滤器:一种用于用户级数据包捕获的新体系结构》)。最初,BPF 是在 BSD 内核实现的,后来,由于其出色的设计思想,其他操作系统也将其引入, 包括 Linux。
现在提到更多的是eBPF(extended BPF,现在的BPF可以做的不仅仅是包过滤,所以可以当作一个专有名词)。它的价值是可以在特权上下文中(如操作系统内核)运行沙盒程序。
字节码
(这里介绍的Linux内核中的实现)eBPF 是一个 RISC 寄存器虚拟机,共有11 个 64 位 寄存器,一个程序计数器和 512 字节的固定大小的栈。
r0: |
存储返回值,包括函数调用和当前程序退出代码 |
---|---|
r1-r5: |
作为函数调用参数使用,在程序启动时,r1 包含 "上下文" 参数指针 |
r6-r9: |
这些在内核函数调用之间被保留下来 |
r10: |
每个 eBPF 程序 512 字节栈的只读指针 |
eBPF 指令也是固定大小的 64 位编码,目前大约有 100 条指令,参考https://github.com/iovisor/bpf-docs/blob/master/eBPF.md。
eBPF 验证器:如果不考虑运行环境(eBPF虚拟机),只看指令集的话,eBPF 是图灵完备的。但是我们会将图灵不完备作为eBPF的主要特征。
- Linux的验证器思路似乎非常简单粗暴:(i) 禁止循环,CFG是有向无环图 (ii) 模拟执行每条路径,验证寄存器的类型。
- PREVAIL 使用Abstract Interpretion来验证程序的性质。
应用
- 在内核中允许沙盒程序,高效的过滤网络报文
- RBPF https://github.com/qmonnet/rbpf
- Solana 智能合约 https://github.com/solana-labs/rbpf