One way to solve the ebpf verifier alert type error problem

Sometimes when we load a compiled eBPF program, the eBPF verifier will indicate that there is a type error in the program causing the program to fail to load. This article documents a solution to this error.

Example of error

For example, when this eBPF program

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;
}

is loaded into the kernel with the following type of error:

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

Solution

This error message has two key errors, one error is

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

Where R1 type=ctx expected=fp means that the verifier expects R1 to be of type fp and not ctx. By fp, we mean the type of the pointer on the stack, i.e., R1 is expected to be data on the stack and not ctx.

Another error is

 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_

Where R2 type=ptr_or_null_ expected=ptr_ means that the verifier expects R2 to be of type ptr and not prt_or_null, that is, it expects R2 to be a pointer and not a pointer or NULL. Here you may be a little confused, the previous judgment if (ctx->sk) has already ensured that it will not be NULL, why here still think it may be NULL, this is because the previous if judgment is not a stack variable, there is R1 type=ctx expected=fp problem also can not guarantee that it is not NULL.

The solution is simple: use a temporary variable to store the value of ctx->sk, and then use this temporary variable on the stack for subsequent operations:

 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;
 }

Meaning of common types of keywords

The specific meaning of common type keywords like the previous fp is documented here.

keyword describe
scalar reg doesn't contain a valid pointer
ctx reg points to bpf_context
map_ptr reg points to struct bpf_map
map_value reg points to map element value
map_value_or_null points to map elem value or NULL
map_key reg points to a map element key
fp reg == frame_pointer + offset
pkt skb->data
pkt_meta skb->data - meta_len
pkt_end skb->data + headlen
sock reg points to struct bpf_sock
sock_or_null reg points to struct bpf_sock or NULL
sock_common reg points to sock_common
sock_common_or_null reg points to sock_common or NULL
tcp_sock reg points to struct tcp_sock
tcp_sock_or_null reg points to struct tcp_sock or NULL
tp_buffer reg points to a writable raw tp's buffer
xdp_sock reg points to struct xdp_sock
ptr_ points to a kernel struct that does not need to be null checked by the BPF program.
ptr_or_null_ points to a kernel struct that has not been checked for null
dynptr_ptr pointer to bpf_dynptr
mem reg points to valid memory region
mem_or_null reg points to valid memory region or NULL
buf reg points to a read/write buffer
func reg points to a bpf program function
inv reg doesn't contain a valid pointer
flow_keys reg points to bpf_flow_keys
percpu_ptr_ reg points to a percpu kernel variable
rdonly_buf reg points to a readonly buffer
rdonly_buf_or_null reg points to a readonly buffer or NULL
rdwr_buf reg points to a read/write buffer
rdwr_buf_or_null reg points to a read/write buffer or NULL

Comments