Search code examples
clinux-kernelbpfxdp-bpf

How to read/understand the bpf kernel verifier analysis to debug the error?


I am new to XDP eBPF. I have a BPF program intended to Drop the UDP packets but it's unable to load since it gets rejected by the kernel verifier. Below is the code:

#include <linux/bpf.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("xdp")
int dropper(struct xdp_md *ctx) {

    unsigned int ipsize = 0;
    void *data = (void *)(unsigned long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    ipsize = sizeof(*eth);
    struct iphdr *ip = (struct iphdr *)((char*)ctx->data + ipsize);
    if (ip->protocol == IPPROTO_UDP) {
             return XDP_DROP;
    }

    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

This is what appears when loading the program:

Prog section 'xdp' rejected: Permission denied (13)!
 - Type:         6
 - Instructions: 30 (0 over limit)
 - License:      GPL

Verifier analysis:

0: (7b) *(u64 *)(r10 -16) = r1
1: (b7) r1 = 0
2: (63) *(u32 *)(r10 -20) = r1
last_idx 2 first_idx 0
regs=2 stack=0 before 1: (b7) r1 = 0
3: (79) r1 = *(u64 *)(r10 -16)
4: (61) r1 = *(u32 *)(r1 +0)
5: (7b) *(u64 *)(r10 -32) = r1
6: (79) r1 = *(u64 *)(r10 -16)
7: (61) r1 = *(u32 *)(r1 +4)
8: (7b) *(u64 *)(r10 -40) = r1
9: (79) r1 = *(u64 *)(r10 -32)
10: (7b) *(u64 *)(r10 -48) = r1
11: (b7) r1 = 14
12: (63) *(u32 *)(r10 -20) = r1
13: (79) r1 = *(u64 *)(r10 -16)
14: (61) r1 = *(u32 *)(r1 +0)
15: (61) r2 = *(u32 *)(r10 -20)
16: (0f) r1 += r2
last_idx 16 first_idx 0
regs=4 stack=0 before 15: (61) r2 = *(u32 *)(r10 -20)
17: (7b) *(u64 *)(r10 -56) = r1
18: (79) r1 = *(u64 *)(r10 -56)
19: (71) r1 = *(u8 *)(r1 +9)
invalid access to packet, off=9 size=1, R1(id=1,off=0,r=0)
R1 offset is outside of the packet
processed 20 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Error fetching program/map!

I figured out the error appears on the UDP check statement if (ip->protocol == IPPROTO_UDP), because when I comment the check, it loads up with no errors.

So, what I need to know that, why the kernel verifier is rejecting the UDP check statement (as mentioned earlier), and what can be done to resolve it to solve the error.

Thanks.


Solution

  • Some of those errors are described in the kernel documentation for eBPF. In your case, see in particular the section for direct packet access.

    The kernel verifier that enforces checks on your program in the Linux kernel ensures that no out-of-bound accesses are attempted. Your program is rejected because it may trigger such out-of-bound access.

    What you need is to check that the length of your packet is at least equal to the length of an IPv4 header, so that the program won't attempt to load beyond the boundary of the packet when reading ip->protocol. Something like this (not tested):

    /*
     * If packet is shorter than a IPv4 header, we cannot
     * dereference and read ip->protocol, and it cannot be
     * UDP anyway: pass the packet to the stack.
     *
     * "ip + 1" is the address at "ip + the length of one
     * additional struct iphdr", in other words, it points
     * to the byte right after the IP header.
     */
    if (ip + 1 > data_end)
            return XDP_PASS;
    
    if (ip->protocol == IPPROTO_UDP) { ...
    

    Please refer to this other answer for more details.