首页 > 解决方案 > ebpf:验证 LD_ABS 和 LD_IND 指令

问题描述

我正在阅读验证程序代码,特别是验证安全性LD_ABSLD_IND指令的部分(check_ld_abs())。正如评论所说,这些指令隐含地期望在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)

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

谢谢。

标签: instructionsbpfebpf

解决方案


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

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

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

内核文档在某种程度上证实了这一点:

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


推荐阅读