ebpf

Invalid access to packet while parsing packet in an eBPF program


I am trying to parse a simple gRPC payload in an eBPF program. The code below shows my eBPF program, which I am trying to attach at TC hook (ingress).

SEC("classifier")
int find_grpc(struct __sk_buff *skb){

    if(skb == NULL) {
        goto EXIT;
    }
    
    context_key_t key = CONTEXT_KEY;
    context_data_t * ctx = bpf_map_lookup_elem(&context_map,&key);  
    void *data_end = (void*)(__u64)skb->data_end;
    void *data = (void *)(__u64)skb->data;

    if(ctx==NULL) {
        goto EXIT;
    }
   
    
    if(ctx->action_index >= MAX_ACTION_LIST) {
        goto EXIT;
    }
    
    find_grpc_t *args = (find_grpc_t*)&(ctx->action_argument[ctx->action_index].find_grpc_args);
    
    if(args==NULL) {
        goto EXIT;
    }
        
    unsigned int flag = 0;

    if (args->offset > 100)
    {
        goto EXIT;
    }
    if (ctx->payload_offset > MAX_PAYLOAD_OFFSET)
    {
        goto EXIT;
    }
    unsigned short field_offset = ctx->payload_offset + args->offset;
    char len = 0;
    uint16_t x;
    if (args->field_index > MAX_IDS)
    {
        goto EXIT;
    }
    
    unsigned short toBeFound = args->field_id[args->field_index];

 LOOK:
    if ((data + field_offset + sizeof(uint16_t)) > data_end)
    {
        goto EXIT;
    }
    
    x = *((uint16_t*) (data + field_offset));
    char y = (x & GRPC_ID_MASK) >> GRPC_ID_SHIFT;
    len = x & GRPC_LEN_MASK;
    if (len > 32)
    {
        goto EXIT;
    }
    if (y == toBeFound)
    {

        goto FOUND;
    }
    field_offset += len;
    goto LOOK;


FOUND:
    // some logic on finding the required attribute in the payload
EXIT:
    return TC_ACT_OK;       
}
#endif 

In the above code, the verifier complains by saying that I am trying to access an offset which is outside the packet at the following line in code (where I try to dereference a pointer at an offset inside the packet)

x = *((uint16_t*) (off));

As can be seen in the code above, I do check for bounds just above that particular line. Any reason why I might be seeing this error even though I have check for packet bounds?

Following is the complete verifier log

libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
; int find_grpc(struct __sk_buff *skb){
0: (bf) r6 = r1
; if(skb == NULL) {
1: (15) if r6 == 0x0 goto pc+82
 R1=ctx(id=0,off=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R10=fp0
2: (b7) r1 = 0
; context_key_t key = CONTEXT_KEY;
3: (63) *(u32 *)(r10 -4) = r1
last_idx 3 first_idx 0
regs=2 stack=0 before 2: (b7) r1 = 0
4: (bf) r2 = r10
; 
5: (07) r2 += -4
; context_data_t * ctx = bpf_map_lookup_elem(&context_map,&key);  
6: (18) r1 = 0xffff97d362338000
8: (85) call bpf_map_lookup_elem#1
9: (bf) r7 = r0
; void *data = (void *)(__u64)skb->data;
10: (61) r1 = *(u32 *)(r6 +76)
; void *data_end = (void*)(__u64)skb->data_end;
11: (61) r2 = *(u32 *)(r6 +80)
; if(ctx==NULL) {
12: (15) if r7 == 0x0 goto pc+71
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_w=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R10=fp0 fp-8=mmmm????
; if(ctx->action_index >= MAX_ACTION_LIST) {
13: (69) r5 = *(u16 *)(r7 +6)
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_w=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R10=fp0 fp-8=mmmm????
; if(ctx->action_index >= MAX_ACTION_LIST) {
14: (25) if r5 > 0x1f goto pc+69
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_w=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R5_w=inv(id=0,umax_value=31,var_off=(0x0; 0x1f)) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R10=fp0 fp-8=mmmm????
; if (args->offset > 100)
15: (27) r5 *= 584
; find_grpc_t *args = (find_grpc_t*)&(ctx->action_argument[ctx->action_index].find_grpc_args);
16: (bf) r4 = r7
17: (07) r4 += 272
; if (args->offset > 100)
18: (bf) r3 = r4
19: (0f) r3 += r5
last_idx 19 first_idx 9
regs=20 stack=0 before 18: (bf) r3 = r4
regs=20 stack=0 before 17: (07) r4 += 272
regs=20 stack=0 before 16: (bf) r4 = r7
regs=20 stack=0 before 15: (27) r5 *= 584
regs=20 stack=0 before 14: (25) if r5 > 0x1f goto pc+69
regs=20 stack=0 before 13: (69) r5 = *(u16 *)(r7 +6)
20: (69) r8 = *(u16 *)(r3 +0)
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_w=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R3_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4_w=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5_w=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R10=fp0 fp-8=mmmm????
; if (args->offset > 100)
21: (25) if r8 > 0x64 goto pc+62
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=inv(id=0,umax_value=100,var_off=(0x0; 0x7f)) R10=fp0 fp-8=mmmm????
; if (ctx->payload_offset > MAX_PAYLOAD_OFFSET)
22: (69) r3 = *(u16 *)(r7 +2)
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=inv(id=0,umax_value=100,var_off=(0x0; 0x7f)) R10=fp0 fp-8=mmmm????
; if (ctx->payload_offset > MAX_PAYLOAD_OFFSET)
23: (25) if r3 > 0x3c0 goto pc+60
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umax_value=960,var_off=(0x0; 0x3ff)) R4=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=inv(id=0,umax_value=100,var_off=(0x0; 0x7f)) R10=fp0 fp-8=mmmm????
; if (args->field_index > MAX_IDS)
24: (0f) r4 += r5
last_idx 24 first_idx 21
regs=20 stack=0 before 23: (25) if r3 > 0x3c0 goto pc+60
regs=20 stack=0 before 22: (69) r3 = *(u16 *)(r7 +2)
regs=20 stack=0 before 21: (25) if r8 > 0x64 goto pc+62
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_w=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R3_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4_rw=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5_rw=invP(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7_rw=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8_rw=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R10=fp0 fp-8=mmmm????
parent didn't have regs=20 stack=0 marks
last_idx 20 first_idx 9
regs=20 stack=0 before 20: (69) r8 = *(u16 *)(r3 +0)
regs=20 stack=0 before 19: (0f) r3 += r5
regs=20 stack=0 before 18: (bf) r3 = r4
regs=20 stack=0 before 17: (07) r4 += 272
regs=20 stack=0 before 16: (bf) r4 = r7
regs=20 stack=0 before 15: (27) r5 *= 584
regs=20 stack=0 before 14: (25) if r5 > 0x1f goto pc+69
regs=20 stack=0 before 13: (69) r5 = *(u16 *)(r7 +6)
25: (69) r0 = *(u16 *)(r4 +14)
 R0_w=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umax_value=960,var_off=(0x0; 0x3ff)) R4_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R5=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=inv(id=0,umax_value=100,var_off=(0x0; 0x7f)) R10=fp0 fp-8=mmmm????
; if (args->field_index > MAX_IDS)
26: (25) if r0 > 0x5 goto pc+57
 R0_w=inv(id=0,umax_value=5,var_off=(0x0; 0x7)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umax_value=960,var_off=(0x0; 0x3ff)) R4_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R5=inv(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=inv(id=0,umax_value=100,var_off=(0x0; 0x7f)) R10=fp0 fp-8=mmmm????
; unsigned short field_offset = ctx->payload_offset + args->offset;
27: (0f) r3 += r8
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
28: (bf) r8 = r3
29: (57) r8 &= 65535
30: (bf) r5 = r1
31: (0f) r5 += r8
last_idx 31 first_idx 21
regs=100 stack=0 before 30: (bf) r5 = r1
regs=100 stack=0 before 29: (57) r8 &= 65535
regs=100 stack=0 before 28: (bf) r8 = r3
regs=8 stack=0 before 27: (0f) r3 += r8
regs=108 stack=0 before 26: (25) if r0 > 0x5 goto pc+57
regs=108 stack=0 before 25: (69) r0 = *(u16 *)(r4 +14)
regs=108 stack=0 before 24: (0f) r4 += r5
regs=108 stack=0 before 23: (25) if r3 > 0x3c0 goto pc+60
regs=108 stack=0 before 22: (69) r3 = *(u16 *)(r7 +2)
regs=100 stack=0 before 21: (25) if r8 > 0x64 goto pc+62
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_rw=pkt(id=0,off=0,r=0,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R3_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4_rw=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5_rw=invP(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6=ctx(id=0,off=0,imm=0) R7_rw=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8_rw=invP(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R10=fp0 fp-8=mmmm????
parent didn't have regs=100 stack=0 marks
last_idx 20 first_idx 9
regs=100 stack=0 before 20: (69) r8 = *(u16 *)(r3 +0)
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
32: (bf) r8 = r5
33: (07) r8 += 2
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
34: (2d) if r8 > r2 goto pc+49
 R0=inv(id=0,umax_value=5,var_off=(0x0; 0x7)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R5=pkt(id=3,off=0,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
35: (67) r0 <<= 1
36: (0f) r4 += r0
last_idx 36 first_idx 34
regs=1 stack=0 before 35: (67) r0 <<= 1
regs=1 stack=0 before 34: (2d) if r8 > r2 goto pc+49
 R0_rw=invP(id=0,umax_value=5,var_off=(0x0; 0x7)) R1=pkt(id=0,off=0,r=0,imm=0) R2_r=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4_rw=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R5_w=pkt(id=3,off=0,r=0,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8_rw=pkt(id=3,off=2,r=0,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
parent didn't have regs=1 stack=0 marks
last_idx 33 first_idx 21
regs=1 stack=0 before 33: (07) r8 += 2
regs=1 stack=0 before 32: (bf) r8 = r5
regs=1 stack=0 before 31: (0f) r5 += r8
regs=1 stack=0 before 30: (bf) r5 = r1
regs=1 stack=0 before 29: (57) r8 &= 65535
regs=1 stack=0 before 28: (bf) r8 = r3
regs=1 stack=0 before 27: (0f) r3 += r8
regs=1 stack=0 before 26: (25) if r0 > 0x5 goto pc+57
regs=1 stack=0 before 25: (69) r0 = *(u16 *)(r4 +14)
37: (69) r4 = *(u16 *)(r4 +4)
 R0_w=inv(id=0,umax_value=10,var_off=(0x0; 0xe)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18114,var_off=(0x0; 0xfffe),s32_max_value=65534,u32_max_value=65534) R5=pkt(id=3,off=0,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
38: (05) goto pc+9
; x = *((uint16_t*) (data + field_offset));
48: (69) r0 = *(u16 *)(r5 +0)
; if (len > 32)
49: (bf) r5 = r0
50: (67) r5 <<= 56
51: (c7) r5 s>>= 32
; if (len > 32)
52: (65) if r5 s> 0x20000000 goto pc+31
 R0=inv(id=4,umax_value=65535,var_off=(0x0; 0xffff)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R5=inv(id=0,smin_value=-2147483648,smax_value=536870912,umax_value=18446744073692774400,var_off=(0x0; 0xffffffffff000000),u32_max_value=-16777216) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
; char y = (x & GRPC_ID_MASK) >> GRPC_ID_SHIFT;
53: (57) r0 &= 61440
54: (77) r0 >>= 12
; if (y == toBeFound)
55: (5d) if r0 != r4 goto pc-17

from 55 to 39: R0_w=inv(id=0,umax_value=15,var_off=(0x0; 0xf)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R5=inv(id=0,smin_value=-2147483648,smax_value=536870912,umax_value=18446744073692774400,var_off=(0x0; 0xffffffffff000000),u32_max_value=-16777216) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
; 
39: (c7) r5 s>>= 24
40: (0f) r3 += r5
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
41: (bf) r0 = r3
42: (57) r0 &= 65535
43: (bf) r5 = r1
44: (0f) r5 += r0
last_idx 44 first_idx 52
regs=1 stack=0 before 43: (bf) r5 = r1
regs=1 stack=0 before 42: (57) r0 &= 65535
regs=1 stack=0 before 41: (bf) r0 = r3
regs=8 stack=0 before 40: (0f) r3 += r5
regs=28 stack=0 before 39: (c7) r5 s>>= 24
regs=28 stack=0 before 55: (5d) if r0 != r4 goto pc-17
regs=28 stack=0 before 54: (77) r0 >>= 12
regs=28 stack=0 before 53: (57) r0 &= 61440
regs=28 stack=0 before 52: (65) if r5 s> 0x20000000 goto pc+31
 R0_rw=inv(id=4,umax_value=65535,var_off=(0x0; 0xffff)) R1_r=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_r=invP(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4_rw=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R5_rw=invP(id=0,smin_value=-2147483648,smax_value=2130706432,umax_value=18446744073692774400,var_off=(0x0; 0xffffffffff000000),u32_max_value=-16777216) R6_r=ctx(id=0,off=0,imm=0) R7_r=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8_r=mmmm????
parent didn't have regs=28 stack=0 marks
last_idx 51 first_idx 34
regs=28 stack=0 before 51: (c7) r5 s>>= 32
regs=28 stack=0 before 50: (67) r5 <<= 56
regs=28 stack=0 before 49: (bf) r5 = r0
regs=9 stack=0 before 48: (69) r0 = *(u16 *)(r5 +0)
regs=8 stack=0 before 38: (05) goto pc+9
regs=8 stack=0 before 37: (69) r4 = *(u16 *)(r4 +4)
regs=8 stack=0 before 36: (0f) r4 += r0
regs=8 stack=0 before 35: (67) r0 <<= 1
regs=8 stack=0 before 34: (2d) if r8 > r2 goto pc+49
 R0_rw=invP(id=0,umax_value=5,var_off=(0x0; 0x7)) R1_r=pkt(id=0,off=0,r=0,imm=0) R2_r=pkt_end(id=0,off=0,imm=0) R3_rw=invP(id=2,umax_value=1060,var_off=(0x0; 0x7ff)) R4_rw=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R5_rw=pkt(id=3,off=0,r=0,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R6_r=ctx(id=0,off=0,imm=0) R7_r=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8_rw=pkt(id=3,off=2,r=0,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8_r=mmmm????
parent didn't have regs=8 stack=0 marks
last_idx 33 first_idx 21
regs=8 stack=0 before 33: (07) r8 += 2
regs=8 stack=0 before 32: (bf) r8 = r5
regs=8 stack=0 before 31: (0f) r5 += r8
regs=8 stack=0 before 30: (bf) r5 = r1
regs=8 stack=0 before 29: (57) r8 &= 65535
regs=8 stack=0 before 28: (bf) r8 = r3
regs=8 stack=0 before 27: (0f) r3 += r8
regs=108 stack=0 before 26: (25) if r0 > 0x5 goto pc+57
regs=108 stack=0 before 25: (69) r0 = *(u16 *)(r4 +14)
regs=108 stack=0 before 24: (0f) r4 += r5
regs=108 stack=0 before 23: (25) if r3 > 0x3c0 goto pc+60
regs=108 stack=0 before 22: (69) r3 = *(u16 *)(r7 +2)
regs=100 stack=0 before 21: (25) if r8 > 0x64 goto pc+62
 R0=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R1_rw=pkt(id=0,off=0,r=0,imm=0) R2_rw=pkt_end(id=0,off=0,imm=0) R3_w=map_value(id=0,off=272,ks=4,vs=19096,umax_value=18104,var_off=(0x0; 0x7ff8),s32_max_value=32760,u32_max_value=32760) R4_rw=map_value(id=0,off=272,ks=4,vs=19096,imm=0) R5_rw=invP(id=0,umax_value=18104,var_off=(0x0; 0x7ff8)) R6_r=ctx(id=0,off=0,imm=0) R7_rw=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8_rw=invP(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R10=fp0 fp-8_r=mmmm????
parent already had regs=100 stack=0 marks
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
45: (bf) r0 = r5
46: (07) r0 += 2
; if ((data + field_offset + sizeof(uint16_t)) > data_end)
47: (2d) if r0 > r2 goto pc+36
 R0_w=pkt(id=6,off=2,r=0,umax_value=65535,var_off=(0x0; 0xffff)) R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=5,smin_value=-128,smax_value=1092) R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R5_w=pkt(id=6,off=0,r=0,umax_value=65535,var_off=(0x0; 0xffff)) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0) R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047) R10=fp0 fp-8=mmmm????
; x = *((uint16_t*) (data + field_offset));
48: (69) r0 = *(u16 *)(r5 +0)
invalid access to packet, off=0 size=2, R5(id=6,off=0,r=0)
R5 offset is outside of the packet
processed 88 insns (limit 1000000) max_states_per_insn 0 total_states 8 peak_states 8 mark_read 5


Solution

  • TL;DR. You are hitting a corner-case limitation of the eBPF verifier, as explained in https://stackoverflow.com/a/70731589/6884590. You will need an extra bounds check on the packet to work around that. Your program will however likely be rejected due to its complexity afterward


    Verifier Error Explanation

    48: (69) r0 = *(u16 *)(r5 +0)
    invalid access to packet, off=0 size=2, R5(id=6,off=0,r=0)
    R5 offset is outside of the packet
    

    The verifier complains on the packet access because the access seems to be out of the known packet bounds. The access is at offset 0, with a size of 2 bytes. The known packet length is 0 (r=0). Hence the program is rejected.

    Maximum Packet Size Limitation

    You did check the packet bounds above, but it wasn't enough because of this corner-case limitation in the verifier:

    if (dst_reg->umax_value > MAX_PACKET_OFF ||
        dst_reg->umax_value + dst_reg->off > MAX_PACKET_OFF)
        /* Risk of overflow.  For instance, ptr + (1<<63) may be less
         * than pkt_end, but that's because it's also less than pkt.
         */
        return;
    

    Potential Workaround

    We can see the impact in the verifier logs:

    ; if ((data + field_offset + sizeof(uint16_t)) > data_end)
    47: (2d) if r0 > r2 goto pc+36
     R0_w=pkt(id=6,off=2,r=0,umax_value=65535,var_off=(0x0; 0xffff))
     R1=pkt(id=0,off=0,r=0,imm=0) R2=pkt_end(id=0,off=0,imm=0)
     R3_w=inv(id=5,smin_value=-128,smax_value=1092)
     R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))
     R5_w=pkt(id=6,off=0,r=0,umax_value=65535,var_off=(0x0; 0xffff))
     R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=19096,imm=0)
     R8=pkt(id=3,off=2,r=2,umax_value=1060,var_off=(0x0; 0x7ff),s32_max_value=2047,u32_max_value=2047)
     R10=fp0 fp-8=mmmm????
    

    After the comparison of R0 (packet pointer) and R2 (packet end pointer), the known packet length (range, or r=) should be set to R0's offset (off=) so 2. However, because R0's umax_value is equal to 65535 and R0's offset is equal to 2, per the above corner-case limitation, the sum is superior to MAX_PACKET_OFF (65535) and the range is therefore not updated. Hence, we end up with r=0 for R0.

    You can contrast this with the comparison of R2 and R8 on instruction 34, where the range is properly updated (r=2).


    To work around that, you will have to convince the verifier that you're pointer has a lower maximum value. The following may work:

    uint16_t *pkt_ptr = data + field_offset;
    if (pkt_ptr + 1 > data_end || pkt_ptr + 1 > MAX_PACKET_OFF)
        goto EXIT;
    x = *pkt_ptr;
    

    Subsequent Errors

    Even if you fix this, I strongly suspect the verifier will reject your program because your loop around the LOOK goto isn't bounded from the verifier's point of view. It is bounded by the packet size, but the length of the packets isn't known at verification time so it doesn't count for the verifier.

    To solve that, you will probably have to add a hardcoded bound on the number of iterations. If that is not enough, you may want to take a look at helpers and kfuncs to implement loops in BPF. Note that parsing gRPC with eBPF is known to be quite challenging so you will likely have to impose a bunch of limits to reach that goal.