lbrndnr
lbrndnr

Reputation: 3389

Invalid access when reading a packet using bpf_loop

I'm trying to parse the first couple of bytes of a packet passed to a stream_parser program. However, I'm running into a few problems that make little sense to me. Here's the program:

    __u32 len = 256;
    // uncommenting this breaks it even using the regular for loop
    // if (len > skb->len) len = skb->len;

    if (bpf_skb_pull_data(skb, len) < 0) {
        return -1;
    }

    char *data_end = (char *)(long)skb->data_end;
    char *data = (char *)(long)skb->data;

    if (data + len > data_end) {
        return -1;
    }

    __u32 k = 0;
    bpf_for(k, 0, len) {     
        __u32 i = k & 0xFF;
        if (i < len && data + i > data_end) break;
        bpf_printk("data[i] = %d", data[i]);
    }

    // this works!
    // for (__u32 i = 0; i < len; i++) {
    //     if (data + i > data_end) break;
    //     bpf_printk("data[i] = %d", data[i]);
    // }

And here's output of the verifier:

0: R1=ctx() R10=fp0
; int bpf_prog_parser(struct __sk_buff *skb) {
0: (bf) r6 = r1                       ; R1=ctx() R6_w=ctx()
; if (bpf_skb_pull_data(skb, len) < 0) {
1: (b7) r2 = 256                      ; R2_w=256
2: (85) call bpf_skb_pull_data#39     ; R0_w=scalar()
3: (b7) r1 = 0                        ; R1_w=0
; if (bpf_skb_pull_data(skb, len) < 0) {
4: (6d) if r1 s> r0 goto pc+50        ; R0_w=scalar(smin=0,umax=0x7fffffffffffffff,var_off=(0x0; 0x7fffffffffffffff)) R1_w=0
; char *data_end = (char *)(long)skb->data_end;
5: (61) r8 = *(u32 *)(r6 +80)         ; R6_w=ctx() R8_w=pkt_end()
; char *data = (char *)(long)skb->data;
6: (61) r9 = *(u32 *)(r6 +76)         ; R6_w=ctx() R9_w=pkt(r=0)
; if (data + len > data_end) {
7: (bf) r1 = r9                       ; R1_w=pkt(r=0) R9_w=pkt(r=0)
8: (07) r1 += 256                     ; R1=pkt(off=256,r=0)
; if (data + len > data_end) {
9: (2d) if r1 > r8 goto pc+45         ; R1=pkt(off=256,r=256) R8=pkt_end()
10: (bf) r7 = r10                     ; R7_w=fp0 R10=fp0
; 
11: (07) r7 += -24                    ; R7_w=fp-24
; bpf_for(k, 0, len) {     
12: (bf) r1 = r7                      ; R1_w=fp-24 R7_w=fp-24
13: (b7) r2 = 0                       ; R2_w=0
14: (b7) r3 = 256                     ; R3_w=256
15: (85) call bpf_iter_num_new#76394          ; R0_w=scalar() fp-24_w=iter_num(ref_id=1,state=active,depth=0) refs=1
; bpf_for(k, 0, len) {     
16: (bf) r1 = r7                      ; R1=fp-24 R7=fp-24 refs=1
17: (85) call bpf_iter_num_next#76396 18: R0_w=rdonly_mem(id=2,ref_obj_id=1,sz=4) R6=ctx() R7=fp-24 R8=pkt_end() R9=pkt(r=256) R10=fp0 fp-24=iter_num(ref_id=1,state=active,depth=1) refs=1
; bpf_for(k, 0, len) {     
18: (15) if r0 == 0x0 goto pc+17      ; R0_w=rdonly_mem(id=2,ref_obj_id=1,sz=4) refs=1
19: (61) r1 = *(u32 *)(r0 +0)         ; R0_w=rdonly_mem(id=2,ref_obj_id=1,sz=4) R1_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) refs=1
; bpf_for(k, 0, len) {     
20: (25) if r1 > 0xff goto pc+15      ; R1_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) refs=1
; if (i < len && data + i > data_end) break;
21: (bf) r2 = r9                      ; R2_w=pkt(r=256) R9=pkt(r=256) refs=1
22: (0f) r2 += r1                     ; R1=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R2=pkt(id=5,r=0,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) refs=1
; if (i < len && data + i > data_end) break;
23: (2d) if r2 > r8 goto pc+12        ; R2=pkt(id=5,r=0,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R8=pkt_end() refs=1
; bpf_printk("data[i] = %d", data[i]);
24: (71) r3 = *(u8 *)(r2 +0)
invalid access to packet, off=0 size=1, R2(id=5,off=0,r=0)
R2 offset is outside of the packet
processed 55 insns (limit 1000000) max_states_per_insn 0 total_states 5 peak_states 5 mark_read 2

I have three questions:

  1. Why does this particular code example not work?
  2. Why does the for loop at the bottom work, but bpf_loop not? I would use the regular for loop but this also doesn't work because the number of instructions explodes.
  3. Finally, at the very top I want to constrain len to skb->len. Doing so breaks the verification for both loop variants. Why is that? After all, the upper limit of 256 still holds.

Essentially, I just want to read the first couple of bytes. I know that the verifier is not perfect but I assume that very basic parsing is possible.

Thanks in advance for any help!

Upvotes: 0

Views: 171

Answers (0)

Related Questions