diff options
Diffstat (limited to 'kernel/bpf/verifier.c')
| -rw-r--r-- | kernel/bpf/verifier.c | 192 | 
1 files changed, 146 insertions, 46 deletions
| diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c5b56c92f8e2..a8a725697bed 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -140,7 +140,7 @@ struct bpf_verifier_stack_elem {  	struct bpf_verifier_stack_elem *next;  }; -#define BPF_COMPLEXITY_LIMIT_INSNS	65536 +#define BPF_COMPLEXITY_LIMIT_INSNS	98304  #define BPF_COMPLEXITY_LIMIT_STACK	1024  #define BPF_MAP_PTR_POISON ((void *)0xeB9F + POISON_POINTER_DELTA) @@ -241,6 +241,12 @@ static void print_verifier_state(struct bpf_verifier_state *state)  		if (reg->max_value != BPF_REGISTER_MAX_RANGE)  			verbose(",max_value=%llu",  				(unsigned long long)reg->max_value); +		if (reg->min_align) +			verbose(",min_align=%u", reg->min_align); +		if (reg->aux_off) +			verbose(",aux_off=%u", reg->aux_off); +		if (reg->aux_off_align) +			verbose(",aux_off_align=%u", reg->aux_off_align);  	}  	for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {  		if (state->stack_slot_type[i] == STACK_SPILL) @@ -457,16 +463,22 @@ static const int caller_saved[CALLER_SAVED_REGS] = {  	BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5  }; +static void mark_reg_not_init(struct bpf_reg_state *regs, u32 regno) +{ +	BUG_ON(regno >= MAX_BPF_REG); + +	memset(®s[regno], 0, sizeof(regs[regno])); +	regs[regno].type = NOT_INIT; +	regs[regno].min_value = BPF_REGISTER_MIN_RANGE; +	regs[regno].max_value = BPF_REGISTER_MAX_RANGE; +} +  static void init_reg_state(struct bpf_reg_state *regs)  {  	int i; -	for (i = 0; i < MAX_BPF_REG; i++) { -		regs[i].type = NOT_INIT; -		regs[i].imm = 0; -		regs[i].min_value = BPF_REGISTER_MIN_RANGE; -		regs[i].max_value = BPF_REGISTER_MAX_RANGE; -	} +	for (i = 0; i < MAX_BPF_REG; i++) +		mark_reg_not_init(regs, i);  	/* frame pointer */  	regs[BPF_REG_FP].type = FRAME_PTR; @@ -492,6 +504,7 @@ static void reset_reg_range_values(struct bpf_reg_state *regs, u32 regno)  {  	regs[regno].min_value = BPF_REGISTER_MIN_RANGE;  	regs[regno].max_value = BPF_REGISTER_MAX_RANGE; +	regs[regno].min_align = 0;  }  static void mark_reg_unknown_value_and_range(struct bpf_reg_state *regs, @@ -779,17 +792,37 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno)  }  static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, -				   int off, int size) +				   int off, int size, bool strict)  { -	if (reg->id && size != 1) { -		verbose("Unknown alignment. Only byte-sized access allowed in packet access.\n"); -		return -EACCES; +	int ip_align; +	int reg_off; + +	/* Byte size accesses are always allowed. */ +	if (!strict || size == 1) +		return 0; + +	reg_off = reg->off; +	if (reg->id) { +		if (reg->aux_off_align % size) { +			verbose("Packet access is only %u byte aligned, %d byte access not allowed\n", +				reg->aux_off_align, size); +			return -EACCES; +		} +		reg_off += reg->aux_off;  	} -	/* skb->data is NET_IP_ALIGN-ed */ -	if ((NET_IP_ALIGN + reg->off + off) % size != 0) { +	/* For platforms that do not have a Kconfig enabling +	 * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS the value of +	 * NET_IP_ALIGN is universally set to '2'.  And on platforms +	 * that do set CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS, we get +	 * to this code only in strict mode where we want to emulate +	 * the NET_IP_ALIGN==2 checking.  Therefore use an +	 * unconditional IP align value of '2'. +	 */ +	ip_align = 2; +	if ((ip_align + reg_off + off) % size != 0) {  		verbose("misaligned packet access off %d+%d+%d size %d\n", -			NET_IP_ALIGN, reg->off, off, size); +			ip_align, reg_off, off, size);  		return -EACCES;  	} @@ -797,9 +830,9 @@ static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg,  }  static int check_val_ptr_alignment(const struct bpf_reg_state *reg, -				   int size) +				   int size, bool strict)  { -	if (size != 1) { +	if (strict && size != 1) {  		verbose("Unknown alignment. Only byte-sized access allowed in value access.\n");  		return -EACCES;  	} @@ -807,16 +840,17 @@ static int check_val_ptr_alignment(const struct bpf_reg_state *reg,  	return 0;  } -static int check_ptr_alignment(const struct bpf_reg_state *reg, +static int check_ptr_alignment(struct bpf_verifier_env *env, +			       const struct bpf_reg_state *reg,  			       int off, int size)  { +	bool strict = env->strict_alignment; +  	switch (reg->type) {  	case PTR_TO_PACKET: -		return IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ? 0 : -		       check_pkt_ptr_alignment(reg, off, size); +		return check_pkt_ptr_alignment(reg, off, size, strict);  	case PTR_TO_MAP_VALUE_ADJ: -		return IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ? 0 : -		       check_val_ptr_alignment(reg, size); +		return check_val_ptr_alignment(reg, size, strict);  	default:  		if (off % size != 0) {  			verbose("misaligned access off %d size %d\n", @@ -849,7 +883,7 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,  	if (size < 0)  		return size; -	err = check_ptr_alignment(reg, off, size); +	err = check_ptr_alignment(env, reg, off, size);  	if (err)  		return err; @@ -883,6 +917,8 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,  							 value_regno);  			/* note that reg.[id|off|range] == 0 */  			state->regs[value_regno].type = reg_type; +			state->regs[value_regno].aux_off = 0; +			state->regs[value_regno].aux_off_align = 0;  		}  	} else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) { @@ -953,6 +989,11 @@ static int check_xadd(struct bpf_verifier_env *env, struct bpf_insn *insn)  	if (err)  		return err; +	if (is_pointer_value(env, insn->src_reg)) { +		verbose("R%d leaks addr into mem\n", insn->src_reg); +		return -EACCES; +	} +  	/* check whether atomic_add can read the memory */  	err = check_mem_access(env, insn->dst_reg, insn->off,  			       BPF_SIZE(insn->code), BPF_READ, -1); @@ -1313,7 +1354,6 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)  	struct bpf_verifier_state *state = &env->cur_state;  	const struct bpf_func_proto *fn = NULL;  	struct bpf_reg_state *regs = state->regs; -	struct bpf_reg_state *reg;  	struct bpf_call_arg_meta meta;  	bool changes_data;  	int i, err; @@ -1380,11 +1420,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)  	}  	/* reset caller saved regs */ -	for (i = 0; i < CALLER_SAVED_REGS; i++) { -		reg = regs + caller_saved[i]; -		reg->type = NOT_INIT; -		reg->imm = 0; -	} +	for (i = 0; i < CALLER_SAVED_REGS; i++) +		mark_reg_not_init(regs, caller_saved[i]);  	/* update return register */  	if (fn->ret_type == RET_INTEGER) { @@ -1455,6 +1492,8 @@ add_imm:  		 */  		dst_reg->off += imm;  	} else { +		bool had_id; +  		if (src_reg->type == PTR_TO_PACKET) {  			/* R6=pkt(id=0,off=0,r=62) R7=imm22; r7 += r6 */  			tmp_reg = *dst_reg;  /* save r7 state */ @@ -1488,14 +1527,23 @@ add_imm:  				src_reg->imm);  			return -EACCES;  		} + +		had_id = (dst_reg->id != 0); +  		/* dst_reg stays as pkt_ptr type and since some positive  		 * integer value was added to the pointer, increment its 'id'  		 */  		dst_reg->id = ++env->id_gen; -		/* something was added to pkt_ptr, set range and off to zero */ +		/* something was added to pkt_ptr, set range to zero */ +		dst_reg->aux_off += dst_reg->off;  		dst_reg->off = 0;  		dst_reg->range = 0; +		if (had_id) +			dst_reg->aux_off_align = min(dst_reg->aux_off_align, +						     src_reg->min_align); +		else +			dst_reg->aux_off_align = src_reg->min_align;  	}  	return 0;  } @@ -1669,6 +1717,13 @@ static void check_reg_overflow(struct bpf_reg_state *reg)  		reg->min_value = BPF_REGISTER_MIN_RANGE;  } +static u32 calc_align(u32 imm) +{ +	if (!imm) +		return 1U << 31; +	return imm - ((imm - 1) & imm); +} +  static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  				    struct bpf_insn *insn)  { @@ -1676,8 +1731,10 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  	s64 min_val = BPF_REGISTER_MIN_RANGE;  	u64 max_val = BPF_REGISTER_MAX_RANGE;  	u8 opcode = BPF_OP(insn->code); +	u32 dst_align, src_align;  	dst_reg = ®s[insn->dst_reg]; +	src_align = 0;  	if (BPF_SRC(insn->code) == BPF_X) {  		check_reg_overflow(®s[insn->src_reg]);  		min_val = regs[insn->src_reg].min_value; @@ -1693,12 +1750,18 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  		    regs[insn->src_reg].type != UNKNOWN_VALUE) {  			min_val = BPF_REGISTER_MIN_RANGE;  			max_val = BPF_REGISTER_MAX_RANGE; +			src_align = 0; +		} else { +			src_align = regs[insn->src_reg].min_align;  		}  	} else if (insn->imm < BPF_REGISTER_MAX_RANGE &&  		   (s64)insn->imm > BPF_REGISTER_MIN_RANGE) {  		min_val = max_val = insn->imm; +		src_align = calc_align(insn->imm);  	} +	dst_align = dst_reg->min_align; +  	/* We don't know anything about what was done to this register, mark it  	 * as unknown.  	 */ @@ -1723,18 +1786,21 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  			dst_reg->min_value += min_val;  		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)  			dst_reg->max_value += max_val; +		dst_reg->min_align = min(src_align, dst_align);  		break;  	case BPF_SUB:  		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)  			dst_reg->min_value -= min_val;  		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)  			dst_reg->max_value -= max_val; +		dst_reg->min_align = min(src_align, dst_align);  		break;  	case BPF_MUL:  		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)  			dst_reg->min_value *= min_val;  		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)  			dst_reg->max_value *= max_val; +		dst_reg->min_align = max(src_align, dst_align);  		break;  	case BPF_AND:  		/* Disallow AND'ing of negative numbers, ain't nobody got time @@ -1746,17 +1812,23 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  		else  			dst_reg->min_value = 0;  		dst_reg->max_value = max_val; +		dst_reg->min_align = max(src_align, dst_align);  		break;  	case BPF_LSH:  		/* Gotta have special overflow logic here, if we're shifting  		 * more than MAX_RANGE then just assume we have an invalid  		 * range.  		 */ -		if (min_val > ilog2(BPF_REGISTER_MAX_RANGE)) +		if (min_val > ilog2(BPF_REGISTER_MAX_RANGE)) {  			dst_reg->min_value = BPF_REGISTER_MIN_RANGE; -		else if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) -			dst_reg->min_value <<= min_val; - +			dst_reg->min_align = 1; +		} else { +			if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) +				dst_reg->min_value <<= min_val; +			if (!dst_reg->min_align) +				dst_reg->min_align = 1; +			dst_reg->min_align <<= min_val; +		}  		if (max_val > ilog2(BPF_REGISTER_MAX_RANGE))  			dst_reg->max_value = BPF_REGISTER_MAX_RANGE;  		else if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) @@ -1766,11 +1838,19 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  		/* RSH by a negative number is undefined, and the BPF_RSH is an  		 * unsigned shift, so make the appropriate casts.  		 */ -		if (min_val < 0 || dst_reg->min_value < 0) +		if (min_val < 0 || dst_reg->min_value < 0) {  			dst_reg->min_value = BPF_REGISTER_MIN_RANGE; -		else +		} else {  			dst_reg->min_value =  				(u64)(dst_reg->min_value) >> min_val; +		} +		if (min_val < 0) { +			dst_reg->min_align = 1; +		} else { +			dst_reg->min_align >>= (u64) min_val; +			if (!dst_reg->min_align) +				dst_reg->min_align = 1; +		}  		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)  			dst_reg->max_value >>= max_val;  		break; @@ -1872,6 +1952,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)  			regs[insn->dst_reg].imm = insn->imm;  			regs[insn->dst_reg].max_value = insn->imm;  			regs[insn->dst_reg].min_value = insn->imm; +			regs[insn->dst_reg].min_align = calc_align(insn->imm);  		}  	} else if (opcode > BPF_END) { @@ -2368,7 +2449,6 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)  {  	struct bpf_reg_state *regs = env->cur_state.regs;  	u8 mode = BPF_MODE(insn->code); -	struct bpf_reg_state *reg;  	int i, err;  	if (!may_access_skb(env->prog->type)) { @@ -2401,11 +2481,8 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)  	}  	/* reset caller saved regs to unreadable */ -	for (i = 0; i < CALLER_SAVED_REGS; i++) { -		reg = regs + caller_saved[i]; -		reg->type = NOT_INIT; -		reg->imm = 0; -	} +	for (i = 0; i < CALLER_SAVED_REGS; i++) +		mark_reg_not_init(regs, caller_saved[i]);  	/* mark destination R0 register as readable, since it contains  	 * the value fetched from the packet @@ -2564,6 +2641,7 @@ peek_stack:  				env->explored_states[t + 1] = STATE_LIST_MARK;  		} else {  			/* conditional jump with two edges */ +			env->explored_states[t] = STATE_LIST_MARK;  			ret = push_insn(t, t + 1, FALLTHROUGH, env);  			if (ret == 1)  				goto peek_stack; @@ -2615,7 +2693,8 @@ err_free:  /* the following conditions reduce the number of explored insns   * from ~140k to ~80k for ultra large programs that use a lot of ptr_to_packet   */ -static bool compare_ptrs_to_packet(struct bpf_reg_state *old, +static bool compare_ptrs_to_packet(struct bpf_verifier_env *env, +				   struct bpf_reg_state *old,  				   struct bpf_reg_state *cur)  {  	if (old->id != cur->id) @@ -2658,7 +2737,7 @@ static bool compare_ptrs_to_packet(struct bpf_reg_state *old,  	 * 'if (R4 > data_end)' and all further insn were already good with r=20,  	 * so they will be good with r=30 and we can prune the search.  	 */ -	if (old->off <= cur->off && +	if (!env->strict_alignment && old->off <= cur->off &&  	    old->off >= old->range && cur->off >= cur->range)  		return true; @@ -2722,8 +2801,14 @@ static bool states_equal(struct bpf_verifier_env *env,  		     rcur->type != NOT_INIT))  			continue; +		/* Don't care about the reg->id in this case. */ +		if (rold->type == PTR_TO_MAP_VALUE_OR_NULL && +		    rcur->type == PTR_TO_MAP_VALUE_OR_NULL && +		    rold->map_ptr == rcur->map_ptr) +			continue; +  		if (rold->type == PTR_TO_PACKET && rcur->type == PTR_TO_PACKET && -		    compare_ptrs_to_packet(rold, rcur)) +		    compare_ptrs_to_packet(env, rold, rcur))  			continue;  		return false; @@ -2856,8 +2941,15 @@ static int do_check(struct bpf_verifier_env *env)  			goto process_bpf_exit;  		} -		if (log_level && do_print_state) { -			verbose("\nfrom %d to %d:", prev_insn_idx, insn_idx); +		if (need_resched()) +			cond_resched(); + +		if (log_level > 1 || (log_level && do_print_state)) { +			if (log_level > 1) +				verbose("%d:", insn_idx); +			else +				verbose("\nfrom %d to %d:", +					prev_insn_idx, insn_idx);  			print_verifier_state(&env->cur_state);  			do_print_state = false;  		} @@ -3495,6 +3587,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)  		log_level = 0;  	} +	env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT); +	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) +		env->strict_alignment = true; +  	ret = replace_map_fd_with_map_ptr(env);  	if (ret < 0)  		goto skip_full_check; @@ -3600,6 +3696,10 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops,  	log_level = 0; +	env->strict_alignment = false; +	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) +		env->strict_alignment = true; +  	env->explored_states = kcalloc(env->prog->len,  				       sizeof(struct bpf_verifier_state_list *),  				       GFP_KERNEL); |