diff options
Diffstat (limited to 'kernel/bpf/verifier.c')
| -rw-r--r-- | kernel/bpf/verifier.c | 129 | 
1 files changed, 109 insertions, 20 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6a86723c5b64..664d93972373 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -504,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].value_from_signed = false;  	regs[regno].min_align = 0;  } @@ -777,12 +778,13 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,  	return -EACCES;  } -static bool is_pointer_value(struct bpf_verifier_env *env, int regno) +static bool __is_pointer_value(bool allow_ptr_leaks, +			       const struct bpf_reg_state *reg)  { -	if (env->allow_ptr_leaks) +	if (allow_ptr_leaks)  		return false; -	switch (env->cur_state.regs[regno].type) { +	switch (reg->type) {  	case UNKNOWN_VALUE:  	case CONST_IMM:  		return false; @@ -791,6 +793,11 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno)  	}  } +static bool is_pointer_value(struct bpf_verifier_env *env, int regno) +{ +	return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]); +} +  static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg,  				   int off, int size, bool strict)  { @@ -1832,10 +1839,24 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  	dst_align = dst_reg->min_align;  	/* We don't know anything about what was done to this register, mark it -	 * as unknown. +	 * as unknown. Also, if both derived bounds came from signed/unsigned +	 * mixed compares and one side is unbounded, we cannot really do anything +	 * with them as boundaries cannot be trusted. Thus, arithmetic of two +	 * regs of such kind will get invalidated bounds on the dst side.  	 */ -	if (min_val == BPF_REGISTER_MIN_RANGE && -	    max_val == BPF_REGISTER_MAX_RANGE) { +	if ((min_val == BPF_REGISTER_MIN_RANGE && +	     max_val == BPF_REGISTER_MAX_RANGE) || +	    (BPF_SRC(insn->code) == BPF_X && +	     ((min_val != BPF_REGISTER_MIN_RANGE && +	       max_val == BPF_REGISTER_MAX_RANGE) || +	      (min_val == BPF_REGISTER_MIN_RANGE && +	       max_val != BPF_REGISTER_MAX_RANGE) || +	      (dst_reg->min_value != BPF_REGISTER_MIN_RANGE && +	       dst_reg->max_value == BPF_REGISTER_MAX_RANGE) || +	      (dst_reg->min_value == BPF_REGISTER_MIN_RANGE && +	       dst_reg->max_value != BPF_REGISTER_MAX_RANGE)) && +	     regs[insn->dst_reg].value_from_signed != +	     regs[insn->src_reg].value_from_signed)) {  		reset_reg_range_values(regs, insn->dst_reg);  		return;  	} @@ -1844,10 +1865,12 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  	 * do our normal operations to the register, we need to set the values  	 * to the min/max since they are undefined.  	 */ -	if (min_val == BPF_REGISTER_MIN_RANGE) -		dst_reg->min_value = BPF_REGISTER_MIN_RANGE; -	if (max_val == BPF_REGISTER_MAX_RANGE) -		dst_reg->max_value = BPF_REGISTER_MAX_RANGE; +	if (opcode != BPF_SUB) { +		if (min_val == BPF_REGISTER_MIN_RANGE) +			dst_reg->min_value = BPF_REGISTER_MIN_RANGE; +		if (max_val == BPF_REGISTER_MAX_RANGE) +			dst_reg->max_value = BPF_REGISTER_MAX_RANGE; +	}  	switch (opcode) {  	case BPF_ADD: @@ -1858,10 +1881,17 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env,  		dst_reg->min_align = min(src_align, dst_align);  		break;  	case BPF_SUB: +		/* If one of our values was at the end of our ranges, then the +		 * _opposite_ value in the dst_reg goes to the end of our range. +		 */ +		if (min_val == BPF_REGISTER_MIN_RANGE) +			dst_reg->max_value = BPF_REGISTER_MAX_RANGE; +		if (max_val == BPF_REGISTER_MAX_RANGE) +			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;  		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE) -			dst_reg->min_value -= min_val; +			dst_reg->min_value -= max_val;  		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE) -			dst_reg->max_value -= max_val; +			dst_reg->max_value -= min_val;  		dst_reg->min_align = min(src_align, dst_align);  		break;  	case BPF_MUL: @@ -2023,6 +2053,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)  			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); +			regs[insn->dst_reg].value_from_signed = false;  		}  	} else if (opcode > BPF_END) { @@ -2198,40 +2229,63 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,  			    struct bpf_reg_state *false_reg, u64 val,  			    u8 opcode)  { +	bool value_from_signed = true; +	bool is_range = true; +  	switch (opcode) {  	case BPF_JEQ:  		/* If this is false then we know nothing Jon Snow, but if it is  		 * true then we know for sure.  		 */  		true_reg->max_value = true_reg->min_value = val; +		is_range = false;  		break;  	case BPF_JNE:  		/* If this is true we know nothing Jon Snow, but if it is false  		 * we know the value for sure;  		 */  		false_reg->max_value = false_reg->min_value = val; +		is_range = false;  		break;  	case BPF_JGT: -		/* Unsigned comparison, the minimum value is 0. */ -		false_reg->min_value = 0; +		value_from_signed = false;  		/* fallthrough */  	case BPF_JSGT: +		if (true_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(true_reg, 0); +		if (false_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(false_reg, 0); +		if (opcode == BPF_JGT) { +			/* Unsigned comparison, the minimum value is 0. */ +			false_reg->min_value = 0; +		}  		/* If this is false then we know the maximum val is val,  		 * otherwise we know the min val is val+1.  		 */  		false_reg->max_value = val; +		false_reg->value_from_signed = value_from_signed;  		true_reg->min_value = val + 1; +		true_reg->value_from_signed = value_from_signed;  		break;  	case BPF_JGE: -		/* Unsigned comparison, the minimum value is 0. */ -		false_reg->min_value = 0; +		value_from_signed = false;  		/* fallthrough */  	case BPF_JSGE: +		if (true_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(true_reg, 0); +		if (false_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(false_reg, 0); +		if (opcode == BPF_JGE) { +			/* Unsigned comparison, the minimum value is 0. */ +			false_reg->min_value = 0; +		}  		/* If this is false then we know the maximum value is val - 1,  		 * otherwise we know the mimimum value is val.  		 */  		false_reg->max_value = val - 1; +		false_reg->value_from_signed = value_from_signed;  		true_reg->min_value = val; +		true_reg->value_from_signed = value_from_signed;  		break;  	default:  		break; @@ -2239,6 +2293,12 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,  	check_reg_overflow(false_reg);  	check_reg_overflow(true_reg); +	if (is_range) { +		if (__is_pointer_value(false, false_reg)) +			reset_reg_range_values(false_reg, 0); +		if (__is_pointer_value(false, true_reg)) +			reset_reg_range_values(true_reg, 0); +	}  }  /* Same as above, but for the case that dst_reg is a CONST_IMM reg and src_reg @@ -2248,41 +2308,64 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,  				struct bpf_reg_state *false_reg, u64 val,  				u8 opcode)  { +	bool value_from_signed = true; +	bool is_range = true; +  	switch (opcode) {  	case BPF_JEQ:  		/* If this is false then we know nothing Jon Snow, but if it is  		 * true then we know for sure.  		 */  		true_reg->max_value = true_reg->min_value = val; +		is_range = false;  		break;  	case BPF_JNE:  		/* If this is true we know nothing Jon Snow, but if it is false  		 * we know the value for sure;  		 */  		false_reg->max_value = false_reg->min_value = val; +		is_range = false;  		break;  	case BPF_JGT: -		/* Unsigned comparison, the minimum value is 0. */ -		true_reg->min_value = 0; +		value_from_signed = false;  		/* fallthrough */  	case BPF_JSGT: +		if (true_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(true_reg, 0); +		if (false_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(false_reg, 0); +		if (opcode == BPF_JGT) { +			/* Unsigned comparison, the minimum value is 0. */ +			true_reg->min_value = 0; +		}  		/*  		 * If this is false, then the val is <= the register, if it is  		 * true the register <= to the val.  		 */  		false_reg->min_value = val; +		false_reg->value_from_signed = value_from_signed;  		true_reg->max_value = val - 1; +		true_reg->value_from_signed = value_from_signed;  		break;  	case BPF_JGE: -		/* Unsigned comparison, the minimum value is 0. */ -		true_reg->min_value = 0; +		value_from_signed = false;  		/* fallthrough */  	case BPF_JSGE: +		if (true_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(true_reg, 0); +		if (false_reg->value_from_signed != value_from_signed) +			reset_reg_range_values(false_reg, 0); +		if (opcode == BPF_JGE) { +			/* Unsigned comparison, the minimum value is 0. */ +			true_reg->min_value = 0; +		}  		/* If this is false then constant < register, if it is true then  		 * the register < constant.  		 */  		false_reg->min_value = val + 1; +		false_reg->value_from_signed = value_from_signed;  		true_reg->max_value = val; +		true_reg->value_from_signed = value_from_signed;  		break;  	default:  		break; @@ -2290,6 +2373,12 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,  	check_reg_overflow(false_reg);  	check_reg_overflow(true_reg); +	if (is_range) { +		if (__is_pointer_value(false, false_reg)) +			reset_reg_range_values(false_reg, 0); +		if (__is_pointer_value(false, true_reg)) +			reset_reg_range_values(true_reg, 0); +	}  }  static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id,  | 
