eBPF Talk: 3.18 kernel 里的 CFG 检查 里,我们学习了如何检查 bpf prog 的控制流程图(CFG,Control Flow Graph)是不是一个有向无环图(DAG,Directed Acyclic Graph)。在本文里,我们将学习 verifier 核心内容。

在该 commit 里,实现了对如下内容的检查:

  1. 无效的指令
  2. 未初始化的寄存器的访问
  3. 未初始化的栈的访问
  4. 非对齐的栈的访问
  5. 超出范围的栈的访问
  6. 非法的调用约定
  7. 指令编码不使用预留的属性

为了检查以上内容,还需要以下两个回调函数进行辅助:

  1. bool (*is_valid_access)(int off, int size, enum bpf_access_type type):检查哪些 ctx 属性是可以访问的(ctx 就是 bpf prog 的第一个参数)。
  2. const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id):返回 func_id 对应的函数原型,用于检查函数调用的合法性。

推荐对该 commit 的内容进行阅读。

do_check() 函数

do_check() 函数里,遍历 bpf prog 的每一条指令,根据指令的类型进行不同的处理。

  1. BPF_LDXBPF_STX``、BPF_STBPF_LD:检查寄存器和栈的访问是否合法。
  2. BPF_JMP:检查 BPF_CALLBPF_JABPF_EXIT 和其它有条件跳转指令等跳转指令的合法性。

小结

以上内容的检查的实现都比较琐碎,基本上是根据每条指令 case by case 地进行检查。以我浅薄的文字功底,只能抛砖引玉了。