@@ -2739,7 +2739,9 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
27392739 regno );
27402740 return - EACCES ;
27412741 }
2742- err = __check_mem_access (env , regno , off , size , reg -> range ,
2742+
2743+ err = reg -> range < 0 ? - EINVAL :
2744+ __check_mem_access (env , regno , off , size , reg -> range ,
27432745 zero_size_allowed );
27442746 if (err ) {
27452747 verbose (env , "R%d offset is outside of the packet\n" , regno );
@@ -4697,6 +4699,32 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
46974699 __clear_all_pkt_pointers (env , vstate -> frame [i ]);
46984700}
46994701
4702+ enum {
4703+ AT_PKT_END = -1 ,
4704+ BEYOND_PKT_END = -2 ,
4705+ };
4706+
4707+ static void mark_pkt_end (struct bpf_verifier_state * vstate , int regn , bool range_open )
4708+ {
4709+ struct bpf_func_state * state = vstate -> frame [vstate -> curframe ];
4710+ struct bpf_reg_state * reg = & state -> regs [regn ];
4711+
4712+ if (reg -> type != PTR_TO_PACKET )
4713+ /* PTR_TO_PACKET_META is not supported yet */
4714+ return ;
4715+
4716+ /* The 'reg' is pkt > pkt_end or pkt >= pkt_end.
4717+ * How far beyond pkt_end it goes is unknown.
4718+ * if (!range_open) it's the case of pkt >= pkt_end
4719+ * if (range_open) it's the case of pkt > pkt_end
4720+ * hence this pointer is at least 1 byte bigger than pkt_end
4721+ */
4722+ if (range_open )
4723+ reg -> range = BEYOND_PKT_END ;
4724+ else
4725+ reg -> range = AT_PKT_END ;
4726+ }
4727+
47004728static void release_reg_references (struct bpf_verifier_env * env ,
47014729 struct bpf_func_state * state ,
47024730 int ref_obj_id )
@@ -6708,7 +6736,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
67086736
67096737static void __find_good_pkt_pointers (struct bpf_func_state * state ,
67106738 struct bpf_reg_state * dst_reg ,
6711- enum bpf_reg_type type , u16 new_range )
6739+ enum bpf_reg_type type , int new_range )
67126740{
67136741 struct bpf_reg_state * reg ;
67146742 int i ;
@@ -6733,8 +6761,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
67336761 enum bpf_reg_type type ,
67346762 bool range_right_open )
67356763{
6736- u16 new_range ;
6737- int i ;
6764+ int new_range , i ;
67386765
67396766 if (dst_reg -> off < 0 ||
67406767 (dst_reg -> off == 0 && range_right_open ))
@@ -6985,6 +7012,67 @@ static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode,
69857012 return is_branch64_taken (reg , val , opcode );
69867013}
69877014
7015+ static int flip_opcode (u32 opcode )
7016+ {
7017+ /* How can we transform "a <op> b" into "b <op> a"? */
7018+ static const u8 opcode_flip [16 ] = {
7019+ /* these stay the same */
7020+ [BPF_JEQ >> 4 ] = BPF_JEQ ,
7021+ [BPF_JNE >> 4 ] = BPF_JNE ,
7022+ [BPF_JSET >> 4 ] = BPF_JSET ,
7023+ /* these swap "lesser" and "greater" (L and G in the opcodes) */
7024+ [BPF_JGE >> 4 ] = BPF_JLE ,
7025+ [BPF_JGT >> 4 ] = BPF_JLT ,
7026+ [BPF_JLE >> 4 ] = BPF_JGE ,
7027+ [BPF_JLT >> 4 ] = BPF_JGT ,
7028+ [BPF_JSGE >> 4 ] = BPF_JSLE ,
7029+ [BPF_JSGT >> 4 ] = BPF_JSLT ,
7030+ [BPF_JSLE >> 4 ] = BPF_JSGE ,
7031+ [BPF_JSLT >> 4 ] = BPF_JSGT
7032+ };
7033+ return opcode_flip [opcode >> 4 ];
7034+ }
7035+
7036+ static int is_pkt_ptr_branch_taken (struct bpf_reg_state * dst_reg ,
7037+ struct bpf_reg_state * src_reg ,
7038+ u8 opcode )
7039+ {
7040+ struct bpf_reg_state * pkt ;
7041+
7042+ if (src_reg -> type == PTR_TO_PACKET_END ) {
7043+ pkt = dst_reg ;
7044+ } else if (dst_reg -> type == PTR_TO_PACKET_END ) {
7045+ pkt = src_reg ;
7046+ opcode = flip_opcode (opcode );
7047+ } else {
7048+ return -1 ;
7049+ }
7050+
7051+ if (pkt -> range >= 0 )
7052+ return -1 ;
7053+
7054+ switch (opcode ) {
7055+ case BPF_JLE :
7056+ /* pkt <= pkt_end */
7057+ fallthrough ;
7058+ case BPF_JGT :
7059+ /* pkt > pkt_end */
7060+ if (pkt -> range == BEYOND_PKT_END )
7061+ /* pkt has at last one extra byte beyond pkt_end */
7062+ return opcode == BPF_JGT ;
7063+ break ;
7064+ case BPF_JLT :
7065+ /* pkt < pkt_end */
7066+ fallthrough ;
7067+ case BPF_JGE :
7068+ /* pkt >= pkt_end */
7069+ if (pkt -> range == BEYOND_PKT_END || pkt -> range == AT_PKT_END )
7070+ return opcode == BPF_JGE ;
7071+ break ;
7072+ }
7073+ return -1 ;
7074+ }
7075+
69887076/* Adjusts the register min/max values in the case that the dst_reg is the
69897077 * variable register that we are working on, and src_reg is a constant or we're
69907078 * simply doing a BPF_K check.
@@ -7148,23 +7236,7 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
71487236 u64 val , u32 val32 ,
71497237 u8 opcode , bool is_jmp32 )
71507238{
7151- /* How can we transform "a <op> b" into "b <op> a"? */
7152- static const u8 opcode_flip [16 ] = {
7153- /* these stay the same */
7154- [BPF_JEQ >> 4 ] = BPF_JEQ ,
7155- [BPF_JNE >> 4 ] = BPF_JNE ,
7156- [BPF_JSET >> 4 ] = BPF_JSET ,
7157- /* these swap "lesser" and "greater" (L and G in the opcodes) */
7158- [BPF_JGE >> 4 ] = BPF_JLE ,
7159- [BPF_JGT >> 4 ] = BPF_JLT ,
7160- [BPF_JLE >> 4 ] = BPF_JGE ,
7161- [BPF_JLT >> 4 ] = BPF_JGT ,
7162- [BPF_JSGE >> 4 ] = BPF_JSLE ,
7163- [BPF_JSGT >> 4 ] = BPF_JSLT ,
7164- [BPF_JSLE >> 4 ] = BPF_JSGE ,
7165- [BPF_JSLT >> 4 ] = BPF_JSGT
7166- };
7167- opcode = opcode_flip [opcode >> 4 ];
7239+ opcode = flip_opcode (opcode );
71687240 /* This uses zero as "not present in table"; luckily the zero opcode,
71697241 * BPF_JA, can't get here.
71707242 */
@@ -7346,13 +7418,15 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
73467418 /* pkt_data' > pkt_end, pkt_meta' > pkt_data */
73477419 find_good_pkt_pointers (this_branch , dst_reg ,
73487420 dst_reg -> type , false);
7421+ mark_pkt_end (other_branch , insn -> dst_reg , true);
73497422 } else if ((dst_reg -> type == PTR_TO_PACKET_END &&
73507423 src_reg -> type == PTR_TO_PACKET ) ||
73517424 (reg_is_init_pkt_pointer (dst_reg , PTR_TO_PACKET ) &&
73527425 src_reg -> type == PTR_TO_PACKET_META )) {
73537426 /* pkt_end > pkt_data', pkt_data > pkt_meta' */
73547427 find_good_pkt_pointers (other_branch , src_reg ,
73557428 src_reg -> type , true);
7429+ mark_pkt_end (this_branch , insn -> src_reg , false);
73567430 } else {
73577431 return false;
73587432 }
@@ -7365,13 +7439,15 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
73657439 /* pkt_data' < pkt_end, pkt_meta' < pkt_data */
73667440 find_good_pkt_pointers (other_branch , dst_reg ,
73677441 dst_reg -> type , true);
7442+ mark_pkt_end (this_branch , insn -> dst_reg , false);
73687443 } else if ((dst_reg -> type == PTR_TO_PACKET_END &&
73697444 src_reg -> type == PTR_TO_PACKET ) ||
73707445 (reg_is_init_pkt_pointer (dst_reg , PTR_TO_PACKET ) &&
73717446 src_reg -> type == PTR_TO_PACKET_META )) {
73727447 /* pkt_end < pkt_data', pkt_data > pkt_meta' */
73737448 find_good_pkt_pointers (this_branch , src_reg ,
73747449 src_reg -> type , false);
7450+ mark_pkt_end (other_branch , insn -> src_reg , true);
73757451 } else {
73767452 return false;
73777453 }
@@ -7384,13 +7460,15 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
73847460 /* pkt_data' >= pkt_end, pkt_meta' >= pkt_data */
73857461 find_good_pkt_pointers (this_branch , dst_reg ,
73867462 dst_reg -> type , true);
7463+ mark_pkt_end (other_branch , insn -> dst_reg , false);
73877464 } else if ((dst_reg -> type == PTR_TO_PACKET_END &&
73887465 src_reg -> type == PTR_TO_PACKET ) ||
73897466 (reg_is_init_pkt_pointer (dst_reg , PTR_TO_PACKET ) &&
73907467 src_reg -> type == PTR_TO_PACKET_META )) {
73917468 /* pkt_end >= pkt_data', pkt_data >= pkt_meta' */
73927469 find_good_pkt_pointers (other_branch , src_reg ,
73937470 src_reg -> type , false);
7471+ mark_pkt_end (this_branch , insn -> src_reg , true);
73947472 } else {
73957473 return false;
73967474 }
@@ -7403,13 +7481,15 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
74037481 /* pkt_data' <= pkt_end, pkt_meta' <= pkt_data */
74047482 find_good_pkt_pointers (other_branch , dst_reg ,
74057483 dst_reg -> type , false);
7484+ mark_pkt_end (this_branch , insn -> dst_reg , true);
74067485 } else if ((dst_reg -> type == PTR_TO_PACKET_END &&
74077486 src_reg -> type == PTR_TO_PACKET ) ||
74087487 (reg_is_init_pkt_pointer (dst_reg , PTR_TO_PACKET ) &&
74097488 src_reg -> type == PTR_TO_PACKET_META )) {
74107489 /* pkt_end <= pkt_data', pkt_data <= pkt_meta' */
74117490 find_good_pkt_pointers (this_branch , src_reg ,
74127491 src_reg -> type , true);
7492+ mark_pkt_end (other_branch , insn -> src_reg , false);
74137493 } else {
74147494 return false;
74157495 }
@@ -7509,6 +7589,10 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
75097589 src_reg -> var_off .value ,
75107590 opcode ,
75117591 is_jmp32 );
7592+ } else if (reg_is_pkt_pointer_any (dst_reg ) &&
7593+ reg_is_pkt_pointer_any (src_reg ) &&
7594+ !is_jmp32 ) {
7595+ pred = is_pkt_ptr_branch_taken (dst_reg , src_reg , opcode );
75127596 }
75137597
75147598 if (pred >= 0 ) {
@@ -7517,7 +7601,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
75177601 */
75187602 if (!__is_pointer_value (false, dst_reg ))
75197603 err = mark_chain_precision (env , insn -> dst_reg );
7520- if (BPF_SRC (insn -> code ) == BPF_X && !err )
7604+ if (BPF_SRC (insn -> code ) == BPF_X && !err &&
7605+ !__is_pointer_value (false, src_reg ))
75217606 err = mark_chain_precision (env , insn -> src_reg );
75227607 if (err )
75237608 return err ;
0 commit comments