ebpf:验证 LD_ABS 和 LD_IND 指令

问题描述

我正在阅读 verifier 代码,特别是验证 SELECT id FROM dbo.yourtable WHERE islab = 1 AND isschool = 1 GROUP BY id HAVING COUNT(CASE class WHEN 1 THEN 1 END) = 1 AND COUNT(CASE class WHEN 2 THEN 1 END) = 1; LD_ABS 指令(check_ld_abs())安全性的部分。正如评论所说,这些指令隐含地期望 LD_IND 寄存器中的输入,即我们必须在此处加载指向 r6 的指针。所以我验证了以下 __sk_buff 类型的程序会被验证者拒绝:

BPF_PROG_TYPE_SOCKET_FILTER

...

struct bpf_insn prog[] = {
    BPF_LD_ABS(BPF_B,offsetof(struct iphdr,protocol)),/* exit with value 0 */
    BPF_MOV64_IMM(BPF_REG_0,0),BPF_EXIT_INSN(),};

即它需要在>> 0: (30) r0 = *(u8 *)skb[9] R6 !read_ok 之前准备r6

LD_ABS

但是,评论中还提到了显式输入在这种情况下,输入可以是任何寄存器)?是否仅由 BPF_MOV64_REG(BPF_REG_6,BPF_REG_1) 使用?与隐式模式有什么区别?

谢谢。

解决方法

这不是隐式或显式的模式。这些指令只需要几个参数,其中一些是隐式的,一些是显式的。

  • 上下文是隐式的,因为正如您所解释的,它必须在 r6 中被引用,这意味着它没有被用户显式传递给指令:您在 r6 中看不到 BPF_LD_ABS(BPF_B,offsetof(struct iphdr,protocol))。指令将期望来自该寄存器的上下文是一个隐式约定。

  • 相比之下,指令使用的源寄存器和立即数是字节码中指令本身的一部分,使它们成为显式参数。

kernel documentation 在某种程度上证实了这一点:

eBPF 有两个非通用指令:(BPF_ABS | | BPF_LD) 和 (BPF_IND | | BPF_LD) 用于访问分组数据。

他们必须从经典继承下来才能有强大的表现 在 eBPF 解释器中运行的套接字过滤器。这些说明只能 当解释器上下文是指向 struct sk_buff 和 有七个隐式操作数。寄存器 R6 是一个隐式输入,必须 包含指向 sk_buff 的指针。寄存器 R0 是一个隐式输出,它包含 从数据包中获取的数据。寄存器 R1-R5 是暂存寄存器 并且不得用于跨 BPF_ABS 存储数据 | BPF_LD 或 BPF_IND | BPF_LD 指令。

提醒:

  • LD_ABS 从绝对地址加载数据,从上下文的开头(存储在 r6 中)开始,并添加包含在 imm 字段中的偏移量。数据存储在 r0(隐式输出)中。它不使用 src 寄存器。

  • LD_IND 执行间接加载,它首先使用 src 寄存器中的(变量)值偏移上下文,然后添加(固定)imm 值作为到达要加载到 r0 中的字节的第二个偏移量。