Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
On 02/23/2017 11:00 PM, William Tu wrote: Hi Daniel, Thanks for the reply! I understand the workaround, can you suggest a way to try it? Should I compile the C source into llvm IR bitcode, add thie "r0 &= 0x", then assembly it to bpf target binary. Hmm, problem with C code is that we might have no control over when llvm decides to spill to stack where this workaround would then be needed. Your suggestion could work, maybe an llvm expert can jump in here if we have some other, easier options at hand? Second case is to preserve the type in verifier when spilled, but will make pruning ineffective to the point that we could see regressions of programs not loading anymore (due to running over BPF_COMPLEXITY_LIMIT_INSNS). states_equal() wrt probing stack space doesn't have much optimizations thus far either; certainly this path would need a careful evaluation. On Wed, Feb 22, 2017 at 9:08 AM, Daniel Borkmannwrote: On 02/17/2017 06:07 PM, William Tu via iovisor-dev wrote: Hi, I encountered another BPF verifier issue related to my previous one https://lists.iovisor.org/pipermail/iovisor-dev/2017-January/000603.html My guess is that when register spills to stack and restore, the state of imm upper zero bits does not get restore? Any comments are appreciated! This time the verifier shows: --- R0=imm0,min_value=0,max_value=0 R1=imm58,min_value=58,max_value=58 R3=pkt(id=0,off=58,r=58) R4=inv61 R5=pkt_end R6=imm144,min_value=144,max_value=144 R7=imm0,min_value=0,max_value=0 R8=ctx R9=pkt(id=0,off=0,r=58) R10=fp 260: (bf) r5 = r6 261: (47) r5 |= 12 262: (bf) r1 = r5 263: (07) r1 += 44 264: (77) r1 >>= 3 265: (7b) *(u64 *)(r10 -64) = r1 266: (bf) r7 = r5 267: (07) r7 += 36 268: (77) r7 >>= 3 269: (bf) r0 = r5 270: (07) r0 += 20 271: (77) r0 >>= 3 272: (bf) r1 = r5 273: (07) r1 += 52 274: (77) r1 >>= 3 275: (77) r6 >>= 3 276: (79) r2 = *(u64 *)(r10 -24) 277: (bf) r2 = r5 278: (77) r2 >>= 3 279: (7b) *(u64 *)(r10 -184) = r2 280: (07) r5 += 180 281: (77) r5 >>= 3 282: (bf) r4 = r9 283: (0f) r4 += r5 284: (47) r5 |= 1 285: (bf) r3 = r9 286: (0f) r3 += r6 287: (bf) r6 = r9 288: (0f) r6 += r1 289: (47) r1 |= 1 290: (bf) r2 = r9 291: (0f) r2 += r0 292: (7b) *(u64 *)(r10 -312) = r2 293: (bf) r2 = r9 294: (79) r0 = *(u64 *)(r10 -184) 295: (0f) r2 += r0 cannot add integer value with 0 upper zero bits to ptr_to_packet Right, so lines 260, 261 r5 is imm type, which in 277 is transferred to r2 and eventually in 279 spilled to stack. check_stack_write() would see that type is not spillable, so it will be stored as regular data to stack (STACK_MISC). Later read in 294 will restore that to r0. check_stack_read() sees that stack was marked as STACK_MISC and marks reg as unknown, where you'll later get the error when added to ptr_to_packet as no overflow protection can be guaranteed (imm is 0). Afaik (Alexei, might correct me if I'm wrong), if we would support spilling imm, this could have a non-trivial increase in complexity, where verifier would need to do a lot more work due to pruning not taking effect. If you look at evaluate_reg_alu(), you could try to work around it for the time being by doing r0 &= 0x right before the r2 += r0. Thanks, Daniel ___ iovisor-dev mailing list iovisor-dev@lists.iovisor.org https://lists.iovisor.org/mailman/listinfo/iovisor-dev
Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
Hi Daniel, Thanks for the reply! I understand the workaround, can you suggest a way to try it? Should I compile the C source into llvm IR bitcode, add thie "r0 &= 0x", then assembly it to bpf target binary. Thanks William On Wed, Feb 22, 2017 at 9:08 AM, Daniel Borkmannwrote: > On 02/17/2017 06:07 PM, William Tu via iovisor-dev wrote: >> >> Hi, >> >> I encountered another BPF verifier issue related to my previous one >> https://lists.iovisor.org/pipermail/iovisor-dev/2017-January/000603.html >> >> My guess is that when register spills to stack and restore, the state >> of imm upper zero bits does not get restore? Any comments are >> appreciated! >> >> This time the verifier shows: >> --- >> R0=imm0,min_value=0,max_value=0 R1=imm58,min_value=58,max_value=58 >> R3=pkt(id=0,off=58,r=58) R4=inv61 R5=pkt_end >> R6=imm144,min_value=144,max_value=144 R7=imm0,min_value=0,max_value=0 >> R8=ctx R9=pkt(id=0,off=0,r=58) R10=fp >> 260: (bf) r5 = r6 >> 261: (47) r5 |= 12 >> 262: (bf) r1 = r5 >> 263: (07) r1 += 44 >> 264: (77) r1 >>= 3 >> 265: (7b) *(u64 *)(r10 -64) = r1 >> 266: (bf) r7 = r5 >> 267: (07) r7 += 36 >> 268: (77) r7 >>= 3 >> 269: (bf) r0 = r5 >> 270: (07) r0 += 20 >> 271: (77) r0 >>= 3 >> 272: (bf) r1 = r5 >> 273: (07) r1 += 52 >> 274: (77) r1 >>= 3 >> 275: (77) r6 >>= 3 >> 276: (79) r2 = *(u64 *)(r10 -24) >> 277: (bf) r2 = r5 >> 278: (77) r2 >>= 3 >> 279: (7b) *(u64 *)(r10 -184) = r2 >> 280: (07) r5 += 180 >> 281: (77) r5 >>= 3 >> 282: (bf) r4 = r9 >> 283: (0f) r4 += r5 >> 284: (47) r5 |= 1 >> 285: (bf) r3 = r9 >> 286: (0f) r3 += r6 >> 287: (bf) r6 = r9 >> 288: (0f) r6 += r1 >> 289: (47) r1 |= 1 >> 290: (bf) r2 = r9 >> 291: (0f) r2 += r0 >> 292: (7b) *(u64 *)(r10 -312) = r2 >> 293: (bf) r2 = r9 >> 294: (79) r0 = *(u64 *)(r10 -184) >> 295: (0f) r2 += r0 >> cannot add integer value with 0 upper zero bits to ptr_to_packet > > > Right, so lines 260, 261 r5 is imm type, which in 277 is transferred > to r2 and eventually in 279 spilled to stack. check_stack_write() would > see that type is not spillable, so it will be stored as regular data > to stack (STACK_MISC). Later read in 294 will restore that to r0. > check_stack_read() sees that stack was marked as STACK_MISC and marks > reg as unknown, where you'll later get the error when added to ptr_to_packet > as no overflow protection can be guaranteed (imm is 0). Afaik (Alexei, > might correct me if I'm wrong), if we would support spilling imm, this > could have a non-trivial increase in complexity, where verifier would > need to do a lot more work due to pruning not taking effect. If you > look at evaluate_reg_alu(), you could try to work around it for the time > being by doing r0 &= 0x right before the r2 += r0. > > Thanks, > Daniel ___ iovisor-dev mailing list iovisor-dev@lists.iovisor.org https://lists.iovisor.org/mailman/listinfo/iovisor-dev
Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
On 01/19/2017 04:53 PM, William Tu wrote: thanks for the patch, it works. Thanks a lot, I'll see to cook a proper patch then. from 52 to 80: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=34) R2=pkt_end R3=inv R4=imm272 R5=inv56,min_value=17,max_value=17 R6=pkt(id=0,off=26,r=34) R10=fp 80: (07) r4 += 71 81: (18) r5 = 0xfff8 83: (5f) r4 &= r5 84: (77) r4 >>= 3 85: (0f) r1 += r4 cannot add integer value with 3 upper zero bits to ptr_to_packet I have no idea why this time the compiler wants to do "and" then "right shift", instead of directly shit right 3 bit. R5's type is UNKNOWN_VALUE, So in addition to your patch, I add But what the compiler is doing here is still correct eventually I assume? Yes, it zero out the least significant 3 bits first, then shift right 3 bits. We could also add BPF_AND, but that r5's type is UNKNOWN_VALUE doesn't seem right if I'm not mistaken. That r5 = 0xfff8 should be a dw imm load, print_bpf_insn() seems only to print first part. So next r4 &= r5 should be purely imm operation as well and thus trackable. Only that we need to teach verifier to keep the dw imm when not used with analyzer_ops (nfp related). Could you try the below (only compile tested here) to see whether the verifier can now analyze that part? Thanks, Daniel kernel/bpf/verifier.c | 35 ++--- tools/testing/selftests/bpf/test_verifier.c | 24 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d60e12c..777ef0a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1567,19 +1567,44 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg = [insn->src_reg]; u8 opcode = BPF_OP(insn->code); - /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or' -* insn. Don't care about overflow or negative values, just add them + /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or'/... +* insn. Don't care about overflow or negative values, just add them. */ if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K) dst_reg->imm += insn->imm; else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm += src_reg->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm -= insn->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm -= src_reg->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm *= insn->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm *= src_reg->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) dst_reg->imm |= insn->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm |= src_reg->imm; + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm &= insn->imm; + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm &= src_reg->imm; Thanks, with this patch. now r5 is CONST_IMM, not UNKNOWN_VALUE. Regards, William + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm >>= insn->imm; + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm >>= src_reg->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm <<= insn->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm <<= src_reg->imm; else mark_reg_unknown_value(regs, insn->dst_reg); return 0; @@ -2225,14 +2250,8 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; if (insn->src_reg == 0) { - /* generic move 64-bit immediate into a register, -* only analyzer needs to collect the ld_imm value. -*/ u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; - if (!env->analyzer_ops) - return 0; - regs[insn->dst_reg].type = CONST_IMM; regs[insn->dst_reg].imm = imm; return 0; diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 1aa7324..d60bd07 100644 ---
Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
On 01/18/2017 06:29 PM, William Tu wrote: Hi Daniel, Thanks for the patch. Yes, it solves my issue. Then there is another related due to BPF_AND LBB0_12: ; if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 64)) { from 52 to 80: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=34) R2=pkt_end R3=inv R4=imm272 R5=inv56,min_value=17,max_value=17 R6=pkt(id=0,off=26,r=34) R10=fp 80: (07) r4 += 71 81: (18) r5 = 0xfff8 83: (5f) r4 &= r5 84: (77) r4 >>= 3 85: (0f) r1 += r4 cannot add integer value with 3 upper zero bits to ptr_to_packet I have no idea why this time the compiler wants to do "and" then "right shift", instead of directly shit right 3 bit. R5's type is UNKNOWN_VALUE, So in addition to your patch, I add But what the compiler is doing here is still correct eventually I assume? We could also add BPF_AND, but that r5's type is UNKNOWN_VALUE doesn't seem right if I'm not mistaken. That r5 = 0xfff8 should be a dw imm load, print_bpf_insn() seems only to print first part. So next r4 &= r5 should be purely imm operation as well and thus trackable. Only that we need to teach verifier to keep the dw imm when not used with analyzer_ops (nfp related). Could you try the below (only compile tested here) to see whether the verifier can now analyze that part? Thanks, Daniel kernel/bpf/verifier.c | 35 ++--- tools/testing/selftests/bpf/test_verifier.c | 24 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d60e12c..777ef0a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1567,19 +1567,44 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg = [insn->src_reg]; u8 opcode = BPF_OP(insn->code); - /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or' -* insn. Don't care about overflow or negative values, just add them + /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or'/... +* insn. Don't care about overflow or negative values, just add them. */ if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K) dst_reg->imm += insn->imm; else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm += src_reg->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm -= insn->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm -= src_reg->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm *= insn->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm *= src_reg->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) dst_reg->imm |= insn->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm |= src_reg->imm; + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm &= insn->imm; + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm &= src_reg->imm; + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm >>= insn->imm; + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm >>= src_reg->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm <<= insn->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm <<= src_reg->imm; else mark_reg_unknown_value(regs, insn->dst_reg); return 0; @@ -2225,14 +2250,8 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; if (insn->src_reg == 0) { - /* generic move 64-bit immediate into a register, -* only analyzer needs to collect the ld_imm value. -*/ u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; - if (!env->analyzer_ops) - return 0; - regs[insn->dst_reg].type = CONST_IMM; regs[insn->dst_reg].imm = imm; return 0; diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 1aa7324..d60bd07 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++
Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
Hi Daniel, Thanks for the patch. Yes, it solves my issue. Then there is another related due to BPF_AND LBB0_12: ; if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 64)) { from 52 to 80: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=34) R2=pkt_end R3=inv R4=imm272 R5=inv56,min_value=17,max_value=17 R6=pkt(id=0,off=26,r=34) R10=fp 80: (07) r4 += 71 81: (18) r5 = 0xfff8 83: (5f) r4 &= r5 84: (77) r4 >>= 3 85: (0f) r1 += r4 cannot add integer value with 3 upper zero bits to ptr_to_packet I have no idea why this time the compiler wants to do "and" then "right shift", instead of directly shit right 3 bit. R5's type is UNKNOWN_VALUE, So in addition to your patch, I add --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1489,11 +1489,46 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) dst_reg->imm |= insn->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm |= src_reg->imm; + + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm &= insn->imm; + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_X && + src_reg->type == CONST_IMM) + dst_reg->imm &= src_reg->imm; + + else if (opcode == BPF_AND && BPF_SRC(insn->code) == BPF_X && + src_reg->type == UNKNOWN_VALUE) + { + dst_reg->min_value &= src_reg->min_value; + dst_reg->max_value &= src_reg->max_value; + } Then it passes verifier. William On Tue, Jan 17, 2017 at 3:16 PM, Daniel Borkmannwrote: > Hi William, > > > On 01/12/2017 07:32 PM, William Tu via iovisor-dev wrote: >> >> Hi, >> I encounter the following BPF verifier error: >> >> from 28 to 30: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=22) >> R2=pkt_end R3=imm144,min_value=144,max_value=144 >> R4=imm0,min_value=0,max_value=0 R5=inv48,min_value=2054,max_value=2054 >> R10=fp >> 30: (bf) r5 = r3 >> 31: (07) r5 += 23 >> 32: (77) r5 >>= 3 >> 33: (bf) r6 = r1 >> 34: (0f) r6 += r5 >> cannot add integer value with 0 upper zero bits to ptr_to_packet >> --- >> Shouldn't the r5 be an imm value? since it comes from r3=144 and just >> doing +=23 and shift right 3 bits? >> --- >> the C code is doing data and data_end checking: >> ebpf_filter: >> ; int ebpf_filter(struct xdp_md* skb){ >> 0: r2 = *(u32 *)(r1 + 4) >> ; void *data = (void *)(long)skb->data; >> 1: r1 = *(u32 *)(r1 + 0) >> ; u32 ebpf_zero = 0; >> 2: r3 = 0 >> 3: *(u32 *)(r10 - 4) = r3 >> 4: r0 = 1 >> ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 48)) { >> 5: r3 = r1 >> 6: r3 += 6 >> 7: if r3 > r2 goto 1 >> 8: goto 1 >> ... >> LBB0_1: >> ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { >>30: r5 = r3 >>31: r5 += 23 >>32: r5 >>= 3 >>33: r6 = r1 >>34: r6 += r5 >>35: if r6 > r2 goto 65509 >> ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { >> ... >> In which I was checking the packet's boundary. BYTE() is a simple macro >> doing >> #define BYTES(w) ((w + 7) >> 3) > > > Can you try out the below patch? > > evaluate_reg_imm_alu() is invoked on ALU ops where the dst reg is > CONST_IMM and the src is either a constant from the insn or a reg > with type CONST_IMM. Currently we only simulate BPF_ADD / BPF_OR > and if we encounter something else (like in your case shifts), then > we mark the reg from imm to unknown, and as soon as you try to add > that to the pkt pointer, then check_packet_ptr_add() will bark due > to src_reg->imm being 0 from prior mark_reg_unknown_value(). Given > we don't care about overflows or negative values on tracking pure > imm ops, we might as well let evaluate_reg_imm_alu() simulate more > ops than just these two. Does that fix it for you? > > Signed-off-by: Daniel Borkmann > --- > kernel/bpf/verifier.c | 24 ++-- > tools/testing/selftests/bpf/test_verifier.c | 24 > 2 files changed, 46 insertions(+), 2 deletions(-) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index d60e12c..2ac50ae 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -1567,19 +1567,39 @@ static int evaluate_reg_imm_alu(struct > bpf_verifier_env *env, > struct bpf_reg_state *src_reg = [insn->src_reg]; > u8 opcode = BPF_OP(insn->code); > > - /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or' > -* insn. Don't care about overflow or negative values, just add them > + /* dst_reg->type == CONST_IMM here, simulate execution of > 'add'/'or'/... > +* insn. Don't care about overflow or negative values, just add > them. > */ >
Re: [iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
Hi William, On 01/12/2017 07:32 PM, William Tu via iovisor-dev wrote: Hi, I encounter the following BPF verifier error: from 28 to 30: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=22) R2=pkt_end R3=imm144,min_value=144,max_value=144 R4=imm0,min_value=0,max_value=0 R5=inv48,min_value=2054,max_value=2054 R10=fp 30: (bf) r5 = r3 31: (07) r5 += 23 32: (77) r5 >>= 3 33: (bf) r6 = r1 34: (0f) r6 += r5 cannot add integer value with 0 upper zero bits to ptr_to_packet --- Shouldn't the r5 be an imm value? since it comes from r3=144 and just doing +=23 and shift right 3 bits? --- the C code is doing data and data_end checking: ebpf_filter: ; int ebpf_filter(struct xdp_md* skb){ 0: r2 = *(u32 *)(r1 + 4) ; void *data = (void *)(long)skb->data; 1: r1 = *(u32 *)(r1 + 0) ; u32 ebpf_zero = 0; 2: r3 = 0 3: *(u32 *)(r10 - 4) = r3 4: r0 = 1 ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 48)) { 5: r3 = r1 6: r3 += 6 7: if r3 > r2 goto 1 8: goto 1 ... LBB0_1: ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { 30: r5 = r3 31: r5 += 23 32: r5 >>= 3 33: r6 = r1 34: r6 += r5 35: if r6 > r2 goto 65509 ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { ... In which I was checking the packet's boundary. BYTE() is a simple macro doing #define BYTES(w) ((w + 7) >> 3) Can you try out the below patch? evaluate_reg_imm_alu() is invoked on ALU ops where the dst reg is CONST_IMM and the src is either a constant from the insn or a reg with type CONST_IMM. Currently we only simulate BPF_ADD / BPF_OR and if we encounter something else (like in your case shifts), then we mark the reg from imm to unknown, and as soon as you try to add that to the pkt pointer, then check_packet_ptr_add() will bark due to src_reg->imm being 0 from prior mark_reg_unknown_value(). Given we don't care about overflows or negative values on tracking pure imm ops, we might as well let evaluate_reg_imm_alu() simulate more ops than just these two. Does that fix it for you? Signed-off-by: Daniel Borkmann--- kernel/bpf/verifier.c | 24 ++-- tools/testing/selftests/bpf/test_verifier.c | 24 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d60e12c..2ac50ae 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1567,19 +1567,39 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg = [insn->src_reg]; u8 opcode = BPF_OP(insn->code); - /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or' -* insn. Don't care about overflow or negative values, just add them + /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or'/... +* insn. Don't care about overflow or negative values, just add them. */ if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K) dst_reg->imm += insn->imm; else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm += src_reg->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm -= insn->imm; + else if (opcode == BPF_SUB && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm -= src_reg->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm *= insn->imm; + else if (opcode == BPF_MUL && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm *= src_reg->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) dst_reg->imm |= insn->imm; else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm |= src_reg->imm; + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm >>= insn->imm; + else if (opcode == BPF_RSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm >>= src_reg->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm <<= insn->imm; + else if (opcode == BPF_LSH && BPF_SRC(insn->code) == BPF_X && +src_reg->type == CONST_IMM) + dst_reg->imm <<= src_reg->imm; else mark_reg_unknown_value(regs, insn->dst_reg); return 0; diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 1aa7324..d60bd07 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2326,6 +2326,30 @@
[iovisor-dev] BPF verifier: cannot add integer value with 0 upper zero bits to ptr_to_packet
Hi, I encounter the following BPF verifier error: from 28 to 30: R0=imm1,min_value=1,max_value=1 R1=pkt(id=0,off=0,r=22) R2=pkt_end R3=imm144,min_value=144,max_value=144 R4=imm0,min_value=0,max_value=0 R5=inv48,min_value=2054,max_value=2054 R10=fp 30: (bf) r5 = r3 31: (07) r5 += 23 32: (77) r5 >>= 3 33: (bf) r6 = r1 34: (0f) r6 += r5 cannot add integer value with 0 upper zero bits to ptr_to_packet --- Shouldn't the r5 be an imm value? since it comes from r3=144 and just doing +=23 and shift right 3 bits? --- the C code is doing data and data_end checking: ebpf_filter: ; int ebpf_filter(struct xdp_md* skb){ 0: r2 = *(u32 *)(r1 + 4) ; void *data = (void *)(long)skb->data; 1: r1 = *(u32 *)(r1 + 0) ; u32 ebpf_zero = 0; 2: r3 = 0 3: *(u32 *)(r10 - 4) = r3 4: r0 = 1 ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 48)) { 5: r3 = r1 6: r3 += 6 7: if r3 > r2 goto 1 8: goto 1 ... LBB0_1: ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { 30: r5 = r3 31: r5 += 23 32: r5 >>= 3 33: r6 = r1 34: r6 += r5 35: if r6 > r2 goto 65509 ; if (data_end < data + BYTES(ebpf_packetOffsetInBits + 16)) { ... In which I was checking the packet's boundary. BYTE() is a simple macro doing #define BYTES(w) ((w + 7) >> 3) Thanks! William ___ iovisor-dev mailing list iovisor-dev@lists.iovisor.org https://lists.iovisor.org/mailman/listinfo/iovisor-dev