ebpf:验证 LD_ABS 和 LD_IND 指令

ebpf: verify LD_ABS and LD_IND instructions

我正在阅读 verifier 代码,特别是验证 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

即它需要在 LD_ABS:

之前准备 r6
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1)

然而,评论还说显式输入(在这种情况下输入可以是任何寄存器)?它只被LD_IND使用吗?与隐式模式有什么区别?

谢谢。

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

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

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

kernel documentation 多少证实了这一点:

eBPF has two non-generic instructions: (BPF_ABS | | BPF_LD) and (BPF_IND | | BPF_LD) which are used to access packet data.

They had to be carried over from classic to have strong performance of socket filters running in eBPF interpreter. These instructions can only be used when interpreter context is a pointer to struct sk_buff and have seven implicit operands. Register R6 is an implicit input that must contain pointer to sk_buff. Register R0 is an implicit output which contains the data fetched from the packet. Registers R1-R5 are scratch registers and must not be used to store the data across BPF_ABS | BPF_LD or BPF_IND | BPF_LD instructions.

提醒一下:

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

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

    中的字节数