diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2024-10-24 18:45:59 -0700 | 
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2024-10-24 18:47:28 -0700 | 
| commit | bfa7b5c98be4bdcf8aaa4e5ca0b91359ea28c05c (patch) | |
| tree | 751b70005cb6641c42e90191f35dac731459a6ec /kernel/bpf/verifier.c | |
| parent | c6fb8030b4baa01c850f99fc6da051b1017edc46 (diff) | |
| parent | ae90f6a6170d7a7a1aa4fddf664fbd093e3023bc (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Cross-merge bpf fixes after downstream PR.
No conflicts.
Adjacent changes in:
include/linux/bpf.h
include/uapi/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/helpers.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/trace/bpf_trace.c
mm/slab_common.c
tools/include/uapi/linux/bpf.h
tools/testing/selftests/bpf/Makefile
Link: https://lore.kernel.org/all/20241024215724.60017-1-daniel@iogearbox.net/
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'kernel/bpf/verifier.c')
| -rw-r--r-- | kernel/bpf/verifier.c | 111 | 
1 files changed, 60 insertions, 51 deletions
| diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1bd0c3f41f2f..797cf3ed32e0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2750,10 +2750,16 @@ static struct btf *__find_kfunc_desc_btf(struct bpf_verifier_env *env,  		b->module = mod;  		b->offset = offset; +		/* sort() reorders entries by value, so b may no longer point +		 * to the right entry after this +		 */  		sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),  		     kfunc_btf_cmp_by_off, NULL); +	} else { +		btf = b->btf;  	} -	return b->btf; + +	return btf;  }  void bpf_free_kfunc_btf_tab(struct bpf_kfunc_btf_tab *tab) @@ -6360,10 +6366,10 @@ static void coerce_reg_to_size_sx(struct bpf_reg_state *reg, int size)  	/* both of s64_max/s64_min positive or negative */  	if ((s64_max >= 0) == (s64_min >= 0)) { -		reg->smin_value = reg->s32_min_value = s64_min; -		reg->smax_value = reg->s32_max_value = s64_max; -		reg->umin_value = reg->u32_min_value = s64_min; -		reg->umax_value = reg->u32_max_value = s64_max; +		reg->s32_min_value = reg->smin_value = s64_min; +		reg->s32_max_value = reg->smax_value = s64_max; +		reg->u32_min_value = reg->umin_value = s64_min; +		reg->u32_max_value = reg->umax_value = s64_max;  		reg->var_off = tnum_range(s64_min, s64_max);  		return;  	} @@ -7459,7 +7465,8 @@ mark:  }  static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, -				   int access_size, bool zero_size_allowed, +				   int access_size, enum bpf_access_type access_type, +				   bool zero_size_allowed,  				   struct bpf_call_arg_meta *meta)  {  	struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; @@ -7471,7 +7478,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,  		return check_packet_access(env, regno, reg->off, access_size,  					   zero_size_allowed);  	case PTR_TO_MAP_KEY: -		if (meta && meta->raw_mode) { +		if (access_type == BPF_WRITE) {  			verbose(env, "R%d cannot write into %s\n", regno,  				reg_type_str(env, reg->type));  			return -EACCES; @@ -7479,15 +7486,13 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,  		return check_mem_region_access(env, regno, reg->off, access_size,  					       reg->map_ptr->key_size, false);  	case PTR_TO_MAP_VALUE: -		if (check_map_access_type(env, regno, reg->off, access_size, -					  meta && meta->raw_mode ? BPF_WRITE : -					  BPF_READ)) +		if (check_map_access_type(env, regno, reg->off, access_size, access_type))  			return -EACCES;  		return check_map_access(env, regno, reg->off, access_size,  					zero_size_allowed, ACCESS_HELPER);  	case PTR_TO_MEM:  		if (type_is_rdonly_mem(reg->type)) { -			if (meta && meta->raw_mode) { +			if (access_type == BPF_WRITE) {  				verbose(env, "R%d cannot write into %s\n", regno,  					reg_type_str(env, reg->type));  				return -EACCES; @@ -7498,7 +7503,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,  					       zero_size_allowed);  	case PTR_TO_BUF:  		if (type_is_rdonly_mem(reg->type)) { -			if (meta && meta->raw_mode) { +			if (access_type == BPF_WRITE) {  				verbose(env, "R%d cannot write into %s\n", regno,  					reg_type_str(env, reg->type));  				return -EACCES; @@ -7526,7 +7531,6 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,  		 * Dynamically check it now.  		 */  		if (!env->ops->convert_ctx_access) { -			enum bpf_access_type atype = meta && meta->raw_mode ? BPF_WRITE : BPF_READ;  			int offset = access_size - 1;  			/* Allow zero-byte read from PTR_TO_CTX */ @@ -7534,7 +7538,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,  				return zero_size_allowed ? 0 : -EACCES;  			return check_mem_access(env, env->insn_idx, regno, offset, BPF_B, -						atype, -1, false, false); +						access_type, -1, false, false);  		}  		fallthrough; @@ -7559,6 +7563,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,   */  static int check_mem_size_reg(struct bpf_verifier_env *env,  			      struct bpf_reg_state *reg, u32 regno, +			      enum bpf_access_type access_type,  			      bool zero_size_allowed,  			      struct bpf_call_arg_meta *meta)  { @@ -7574,15 +7579,12 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,  	 */  	meta->msize_max_value = reg->umax_value; -	/* The register is SCALAR_VALUE; the access check -	 * happens using its boundaries. +	/* The register is SCALAR_VALUE; the access check happens using +	 * its boundaries. For unprivileged variable accesses, disable +	 * raw mode so that the program is required to initialize all +	 * the memory that the helper could just partially fill up.  	 */  	if (!tnum_is_const(reg->var_off)) -		/* For unprivileged variable accesses, disable raw -		 * mode so that the program is required to -		 * initialize all the memory that the helper could -		 * just partially fill up. -		 */  		meta = NULL;  	if (reg->smin_value < 0) { @@ -7602,9 +7604,8 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,  			regno);  		return -EACCES;  	} -	err = check_helper_mem_access(env, regno - 1, -				      reg->umax_value, -				      zero_size_allowed, meta); +	err = check_helper_mem_access(env, regno - 1, reg->umax_value, +				      access_type, zero_size_allowed, meta);  	if (!err)  		err = mark_chain_precision(env, regno);  	return err; @@ -7615,13 +7616,11 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg  {  	bool may_be_null = type_may_be_null(reg->type);  	struct bpf_reg_state saved_reg; -	struct bpf_call_arg_meta meta;  	int err;  	if (register_is_null(reg))  		return 0; -	memset(&meta, 0, sizeof(meta));  	/* Assuming that the register contains a value check if the memory  	 * access is safe. Temporarily save and restore the register's state as  	 * the conversion shouldn't be visible to a caller. @@ -7631,10 +7630,8 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg  		mark_ptr_not_null_reg(reg);  	} -	err = check_helper_mem_access(env, regno, mem_size, true, &meta); -	/* Check access for BPF_WRITE */ -	meta.raw_mode = true; -	err = err ?: check_helper_mem_access(env, regno, mem_size, true, &meta); +	err = check_helper_mem_access(env, regno, mem_size, BPF_READ, true, NULL); +	err = err ?: check_helper_mem_access(env, regno, mem_size, BPF_WRITE, true, NULL);  	if (may_be_null)  		*reg = saved_reg; @@ -7660,13 +7657,12 @@ static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg  		mark_ptr_not_null_reg(mem_reg);  	} -	err = check_mem_size_reg(env, reg, regno, true, &meta); -	/* Check access for BPF_WRITE */ -	meta.raw_mode = true; -	err = err ?: check_mem_size_reg(env, reg, regno, true, &meta); +	err = check_mem_size_reg(env, reg, regno, BPF_READ, true, &meta); +	err = err ?: check_mem_size_reg(env, reg, regno, BPF_WRITE, true, &meta);  	if (may_be_null)  		*mem_reg = saved_reg; +  	return err;  } @@ -8969,9 +8965,8 @@ skip_type_check:  			verbose(env, "invalid map_ptr to access map->key\n");  			return -EACCES;  		} -		err = check_helper_mem_access(env, regno, -					      meta->map_ptr->key_size, false, -					      NULL); +		err = check_helper_mem_access(env, regno, meta->map_ptr->key_size, +					      BPF_READ, false, NULL);  		break;  	case ARG_PTR_TO_MAP_VALUE:  		if (type_may_be_null(arg_type) && register_is_null(reg)) @@ -8986,9 +8981,9 @@ skip_type_check:  			return -EACCES;  		}  		meta->raw_mode = arg_type & MEM_UNINIT; -		err = check_helper_mem_access(env, regno, -					      meta->map_ptr->value_size, false, -					      meta); +		err = check_helper_mem_access(env, regno, meta->map_ptr->value_size, +					      arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, +					      false, meta);  		break;  	case ARG_PTR_TO_PERCPU_BTF_ID:  		if (!reg->btf_id) { @@ -9030,7 +9025,9 @@ skip_type_check:  		 */  		meta->raw_mode = arg_type & MEM_UNINIT;  		if (arg_type & MEM_FIXED_SIZE) { -			err = check_helper_mem_access(env, regno, fn->arg_size[arg], false, meta); +			err = check_helper_mem_access(env, regno, fn->arg_size[arg], +						      arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, +						      false, meta);  			if (err)  				return err;  			if (arg_type & MEM_ALIGNED) @@ -9038,10 +9035,16 @@ skip_type_check:  		}  		break;  	case ARG_CONST_SIZE: -		err = check_mem_size_reg(env, reg, regno, false, meta); +		err = check_mem_size_reg(env, reg, regno, +					 fn->arg_type[arg - 1] & MEM_WRITE ? +					 BPF_WRITE : BPF_READ, +					 false, meta);  		break;  	case ARG_CONST_SIZE_OR_ZERO: -		err = check_mem_size_reg(env, reg, regno, true, meta); +		err = check_mem_size_reg(env, reg, regno, +					 fn->arg_type[arg - 1] & MEM_WRITE ? +					 BPF_WRITE : BPF_READ, +					 true, meta);  		break;  	case ARG_PTR_TO_DYNPTR:  		err = process_dynptr_func(env, regno, insn_idx, arg_type, 0); @@ -14296,12 +14299,13 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,  	 * r1 += 0x1  	 * if r2 < 1000 goto ...  	 * use r1 in memory access -	 * So remember constant delta between r2 and r1 and update r1 after -	 * 'if' condition. +	 * So for 64-bit alu remember constant delta between r2 and r1 and +	 * update r1 after 'if' condition.  	 */ -	if (env->bpf_capable && BPF_OP(insn->code) == BPF_ADD && -	    dst_reg->id && is_reg_const(src_reg, alu32)) { -		u64 val = reg_const_value(src_reg, alu32); +	if (env->bpf_capable && +	    BPF_OP(insn->code) == BPF_ADD && !alu32 && +	    dst_reg->id && is_reg_const(src_reg, false)) { +		u64 val = reg_const_value(src_reg, false);  		if ((dst_reg->id & BPF_ADD_CONST) ||  		    /* prevent overflow in sync_linked_regs() later */ @@ -15358,8 +15362,12 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s  			continue;  		if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) ||  		    reg->off == known_reg->off) { +			s32 saved_subreg_def = reg->subreg_def; +  			copy_register_state(reg, known_reg); +			reg->subreg_def = saved_subreg_def;  		} else { +			s32 saved_subreg_def = reg->subreg_def;  			s32 saved_off = reg->off;  			fake_reg.type = SCALAR_VALUE; @@ -15372,6 +15380,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s  			 * otherwise another sync_linked_regs() will be incorrect.  			 */  			reg->off = saved_off; +			reg->subreg_def = saved_subreg_def;  			scalar32_min_max_add(reg, &fake_reg);  			scalar_min_max_add(reg, &fake_reg); @@ -21230,7 +21239,7 @@ patch_map_ops_generic:  			delta    += cnt - 1;  			env->prog = prog = new_prog;  			insn      = new_prog->insnsi + i + delta; -			continue; +			goto next_insn;  		}  		/* Implement bpf_kptr_xchg inline */ @@ -22339,7 +22348,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3  	/* 'struct bpf_verifier_env' can be global, but since it's not small,  	 * allocate/free it every time bpf_check() is called  	 */ -	env = kzalloc(sizeof(struct bpf_verifier_env), GFP_KERNEL); +	env = kvzalloc(sizeof(struct bpf_verifier_env), GFP_KERNEL);  	if (!env)  		return -ENOMEM; @@ -22575,6 +22584,6 @@ err_unlock:  		mutex_unlock(&bpf_verifier_lock);  	vfree(env->insn_aux_data);  err_free_env: -	kfree(env); +	kvfree(env);  	return ret;  } | 
