diff options
Diffstat (limited to 'kernel/bpf/verifier.c')
| -rw-r--r-- | kernel/bpf/verifier.c | 88 | 
1 files changed, 54 insertions, 34 deletions
| diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0763cca139a7..d7678d8a925c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6085,6 +6085,27 @@ static int set_map_elem_callback_state(struct bpf_verifier_env *env,  	return 0;  } +static int set_loop_callback_state(struct bpf_verifier_env *env, +				   struct bpf_func_state *caller, +				   struct bpf_func_state *callee, +				   int insn_idx) +{ +	/* bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, +	 *	    u64 flags); +	 * callback_fn(u32 index, void *callback_ctx); +	 */ +	callee->regs[BPF_REG_1].type = SCALAR_VALUE; +	callee->regs[BPF_REG_2] = caller->regs[BPF_REG_3]; + +	/* unused */ +	__mark_reg_not_init(env, &callee->regs[BPF_REG_3]); +	__mark_reg_not_init(env, &callee->regs[BPF_REG_4]); +	__mark_reg_not_init(env, &callee->regs[BPF_REG_5]); + +	callee->in_callback_fn = true; +	return 0; +} +  static int set_timer_callback_state(struct bpf_verifier_env *env,  				    struct bpf_func_state *caller,  				    struct bpf_func_state *callee, @@ -6458,13 +6479,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn  			return err;  	} -	if (func_id == BPF_FUNC_tail_call) { -		err = check_reference_leak(env); -		if (err) { -			verbose(env, "tail_call would lead to reference leak\n"); -			return err; -		} -	} else if (is_release_function(func_id)) { +	if (is_release_function(func_id)) {  		err = release_reference(env, meta.ref_obj_id);  		if (err) {  			verbose(env, "func %s#%d reference has not been acquired before\n", @@ -6475,42 +6490,47 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn  	regs = cur_regs(env); -	/* check that flags argument in get_local_storage(map, flags) is 0, -	 * this is required because get_local_storage() can't return an error. -	 */ -	if (func_id == BPF_FUNC_get_local_storage && -	    !register_is_null(®s[BPF_REG_2])) { -		verbose(env, "get_local_storage() doesn't support non-zero flags\n"); -		return -EINVAL; -	} - -	if (func_id == BPF_FUNC_for_each_map_elem) { +	switch (func_id) { +	case BPF_FUNC_tail_call: +		err = check_reference_leak(env); +		if (err) { +			verbose(env, "tail_call would lead to reference leak\n"); +			return err; +		} +		break; +	case BPF_FUNC_get_local_storage: +		/* check that flags argument in get_local_storage(map, flags) is 0, +		 * this is required because get_local_storage() can't return an error. +		 */ +		if (!register_is_null(®s[BPF_REG_2])) { +			verbose(env, "get_local_storage() doesn't support non-zero flags\n"); +			return -EINVAL; +		} +		break; +	case BPF_FUNC_for_each_map_elem:  		err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,  					set_map_elem_callback_state); -		if (err < 0) -			return -EINVAL; -	} - -	if (func_id == BPF_FUNC_timer_set_callback) { +		break; +	case BPF_FUNC_timer_set_callback:  		err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,  					set_timer_callback_state); -		if (err < 0) -			return -EINVAL; -	} - -	if (func_id == BPF_FUNC_find_vma) { +		break; +	case BPF_FUNC_find_vma:  		err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,  					set_find_vma_callback_state); -		if (err < 0) -			return -EINVAL; -	} - -	if (func_id == BPF_FUNC_snprintf) { +		break; +	case BPF_FUNC_snprintf:  		err = check_bpf_snprintf_call(env, regs); -		if (err < 0) -			return err; +		break; +	case BPF_FUNC_loop: +		err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, +					set_loop_callback_state); +		break;  	} +	if (err) +		return err; +  	/* reset caller saved regs */  	for (i = 0; i < CALLER_SAVED_REGS; i++) {  		mark_reg_not_init(env, regs, caller_saved[i]); |