问题描述
我正在阅读 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
中的字节的第二个偏移量。