每周分享,很水轻喷

bpf_insn

bpf_insn是一个结构体,代表一条bpf指令

1
2
3
4
5
6
7
struct bpf_insn {
__u8 code; /* opcode */
__u8 dst_reg:4; /* dest register */
__u8 src_reg:4; /* source register */
__s16 off; /* signed offset */
__s32 imm; /* signed immediate constant */
};

其中后三个字段并不总是被使用的,例如使用mov操作对寄存器赋值立即数的指令中,src_regoff字段是无用的,须为0

prog

一般的编写方式是直接使用一个bpf_insn数组作为一个完整程序加载到内核中,在Kernel的源码目录中的/samples/bpf/bpf_insn.h文件中有宏定义了大部分指令,可以参考https://elixir.bootlin.com/linux/v5.6/source/samples/bpf/bpf_insn.h#L1

还有相关的eBPF包括编写、加载到内核、使用的示例

https://elixir.bootlin.com/linux/v5.6/source/samples/bpf/sock_example.c

检查

eBPF程序在载入内核之前,会先进行几段检查

check_cfg

利用程序流程图,主要负责检查程序是否有独立的块,即无法到达的块

do_check

模拟执行程序,检查是否有违法操作、有死循环等

eBPF模块可能存在的漏洞点

除去上篇文章分析的整数溢出这种漏洞外,我所了解的另外两个漏洞都是关于,内核在模拟执行eBPF程序的过程中与实际执行程序的过程中的某些操作略有不同,导致在检查过程中有些条件恒成立,如果该条件恒成立,那么检查的时候就不会去检查不成立的那一分支。而如果检查过程中恒成立的条件在实际执行的过程中可以构成不成立的情况,那么就可以执行非法代码,完成任意读写等操作

一些参考学习的资料

https://www.kernel.org/doc/Documentation/networking/filter.txt

http://www.man7.org/linux/man-pages/man2/bpf.2.html

https://elixir.bootlin.com/linux/v5.6/source/samples/bpf/