6

解决 ebpf 验证器提示类型错误的问题

 1 year ago
source link: https://mozillazg.com/2022/10/ebpf-libbpf-verifier-fix-type-expected-issue.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

错误示例

比如,当下面这段 eBPF 程序

SEC("iter/bpf_sk_storage_map")
int iter__bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx)
{
    if (ctx->sk)
        bpf_sk_storage_delete(&sk_storage_map, ctx->sk);

    return 0;
}

被加载到内核中时会提示如下类型错误:

libbpf: prog 'iter__bpf_sk_storage_map': BPF program load failed: Permission denied
libbpf: prog 'iter__bpf_sk_storage_map': -- BEGIN PROG LOAD LOG --
R1 type=ctx expected=fp
; if (ctx->sk)
0: (79) r2 = *(u64 *)(r1 +16)
; if (ctx->sk)
1: (15) if r2 == 0x0 goto pc+4
 R1=ctx(id=0,off=0,imm=0) R2_w=ptr_sock(id=0,off=0,imm=0) R10=fp0
; bpf_sk_storage_delete(&sk_storage_map, ctx->sk);
2: (79) r2 = *(u64 *)(r1 +16)
; bpf_sk_storage_delete(&sk_storage_map, ctx->sk);
3: (18) r1 = 0xffffa0658305aa00
5: (85) call bpf_sk_storage_delete#108
R2 type=ptr_or_null_ expected=ptr_
processed 5 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'iter__bpf_sk_storage_map': failed to load: -13
libbpf: failed to load object 'main.bpf.o'
failed to load BPF object: permission denied

解决办法

这个错误信息有两个关键错误,一个错误是:

R1 type=ctx expected=fp
; if (ctx->sk)
0: (79) r2 = *(u64 *)(r1 +16)
; if (ctx->sk)
1: (15) if r2 == 0x0 goto pc+4

其中 R1 type=ctx expected=fp 说的是,验证器期望 R1 的类型是 fp 而不是 ctx 。 所谓的 fp 指的是栈上的指针类型,即期望 R1 是栈上的数据而不是 ctx 。

另一个错误是:

 R1=ctx(id=0,off=0,imm=0) R2_w=ptr_sock(id=0,off=0,imm=0) R10=fp0
; bpf_sk_storage_delete(&sk_storage_map, ctx->sk);
2: (79) r2 = *(u64 *)(r1 +16)
; bpf_sk_storage_delete(&sk_storage_map, ctx->sk);
3: (18) r1 = 0xffffa0658305aa00
5: (85) call bpf_sk_storage_delete#108
R2 type=ptr_or_null_ expected=ptr_

其中 R2 type=ptr_or_null_ expected=ptr_ 说的是,验证器期望 R2 的类型是 ptr 而不是 prt_or_null ,即,期望 R2 是一个指针而不是一个指针或 NULL 。 这里可能会有点疑惑,前面的判断 if (ctx->sk) 已经确保了不会为 NULL , 为啥这里还会认为它有可能为 NULL ,这是因为前面的 if 判断的不是栈变量, 存在 R1 type=ctx expected=fp 的问题也就无法保证它一定不是 NULL 了。

解决办法也很简单,就是用一个临时变量保存 ctx->sk 的值, 然后用这个栈上的临时变量做后续的操作:

 SEC("iter/bpf_sk_storage_map")
 int iter__bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx)
 {
-    if (ctx->sk)
-        bpf_sk_storage_delete(&sk_storage_map, ctx->sk);
+    struct sock *sk = ctx->sk;
+    if (sk)
+        bpf_sk_storage_delete(&sk_storage_map, sk);

     return 0;
 }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK