All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Vernet <void@manifault.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: davem@davemloft.net, daniel@iogearbox.net, andrii@kernel.org,
	martin.lau@kernel.org, davemarchevsky@meta.com, tj@kernel.org,
	memxor@gmail.com, netdev@vger.kernel.org, bpf@vger.kernel.org,
	kernel-team@fb.com
Subject: Re: [PATCH v4 bpf-next 6/6] bpf: Refactor RCU enforcement in the verifier.
Date: Wed, 1 Mar 2023 22:05:19 -0600	[thread overview]
Message-ID: <ZAAgfwgo5GU8V28f@maniforge> (raw)
In-Reply-To: <20230301223555.84824-7-alexei.starovoitov@gmail.com>

On Wed, Mar 01, 2023 at 02:35:55PM -0800, Alexei Starovoitov wrote:
> From: Alexei Starovoitov <ast@kernel.org>
> 
> bpf_rcu_read_lock/unlock() are only available in clang compiled kernels. Lack
> of such key mechanism makes it impossible for sleepable bpf programs to use RCU
> pointers.
> 
> Allow bpf_rcu_read_lock/unlock() in GCC compiled kernels (though GCC doesn't
> support btf_type_tag yet) and allowlist certain field dereferences in important
> data structures like tast_struct, cgroup, socket that are used by sleepable
> programs either as RCU pointer or full trusted pointer (which is valid outside
> of RCU CS). Use BTF_TYPE_SAFE_RCU and BTF_TYPE_SAFE_TRUSTED macros for such
> tagging. They will be removed once GCC supports btf_type_tag.
> 
> With that refactor check_ptr_to_btf_access(). Make it strict in enforcing
> PTR_TRUSTED and PTR_UNTRUSTED while deprecating old PTR_TO_BTF_ID without
> modifier flags. There is a chance that this strict enforcement might break
> existing programs (especially on GCC compiled kernels), but this cleanup has to
> start sooner than later. Note PTR_TO_CTX access still yields old deprecated
> PTR_TO_BTF_ID. Once it's converted to strict PTR_TRUSTED or PTR_UNTRUSTED the
> kfuncs and helpers will be able to default to KF_TRUSTED_ARGS. KF_RCU will
> remain as a weaker version of KF_TRUSTED_ARGS where obj refcnt could be 0.
> 
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

Nice, this is a great cleanup. Looks good overall, left a couple of
minor comments / questions below.

> ---
>  include/linux/bpf.h                           |   2 +-
>  include/linux/bpf_verifier.h                  |   1 -
>  kernel/bpf/btf.c                              |  15 +-
>  kernel/bpf/cpumask.c                          |  40 ++--
>  kernel/bpf/verifier.c                         | 178 ++++++++++++------
>  .../bpf/prog_tests/cgrp_local_storage.c       |  14 +-
>  .../selftests/bpf/prog_tests/rcu_read_lock.c  |  16 +-
>  .../selftests/bpf/progs/cgrp_ls_sleepable.c   |   4 +-
>  .../selftests/bpf/progs/cpumask_failure.c     |   2 +-
>  .../bpf/progs/nested_trust_failure.c          |   2 +-
>  .../selftests/bpf/progs/rcu_read_lock.c       |   6 +-
>  tools/testing/selftests/bpf/verifier/calls.c  |   2 +-
>  12 files changed, 172 insertions(+), 110 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 23ec684e660d..d3456804f7aa 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2279,7 +2279,7 @@ struct bpf_core_ctx {
>  
>  bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
>  				const struct bpf_reg_state *reg,
> -				int off);
> +				int off, const char *suffix);
>  
>  bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,
>  			       const struct btf *reg_btf, u32 reg_id,
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index b26ff2a8f63b..18538bad2b8c 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -537,7 +537,6 @@ struct bpf_verifier_env {
>  	bool bypass_spec_v1;
>  	bool bypass_spec_v4;
>  	bool seen_direct_write;
> -	bool rcu_tag_supported;
>  	struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
>  	const struct bpf_line_info *prev_linfo;
>  	struct bpf_verifier_log log;
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index c5e1d6955491..bae384728ec7 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -6329,6 +6329,15 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
>  		 * of this field or inside of this struct
>  		 */
>  		if (btf_type_is_struct(mtype)) {
> +			if (BTF_INFO_KIND(mtype->info) == BTF_KIND_UNION &&
> +			    btf_type_vlen(mtype) != 1)
> +				/*
> +				 * walking unions yields untrusted pointers
> +				 * with exception of __bpf_md_ptr and others

s/others/other

> +				 * unions with a single member
> +				 */
> +				*flag |= PTR_UNTRUSTED;
> +
>  			/* our field must be inside that union or struct */
>  			t = mtype;
>  
> @@ -6373,7 +6382,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
>  			stype = btf_type_skip_modifiers(btf, mtype->type, &id);
>  			if (btf_type_is_struct(stype)) {
>  				*next_btf_id = id;
> -				*flag = tmp_flag;
> +				*flag |= tmp_flag;

Now that this function doesn't do a full write of the variable, the
semantics have changed such that the caller has to initialize the
variable on behalf of bpf_struct_walk(). This makes callers such as
btf_struct_ids_match() (line 6503) have random crud in the pointer.
Doesn't really matter for that case because the variable isn't used
anyways, but it seems slightly less brittle to initialize *flag to 0 at
the beginning of the function to avoid requiring the caller to
initialize it. Wdyt?

>  				return WALK_PTR;
>  			}
>  		}
> @@ -8357,7 +8366,7 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
>  
>  bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
>  				const struct bpf_reg_state *reg,
> -				int off)
> +				int off, const char *suffix)
>  {
>  	struct btf *btf = reg->btf;
>  	const struct btf_type *walk_type, *safe_type;
> @@ -8374,7 +8383,7 @@ bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,
>  
>  	tname = btf_name_by_offset(btf, walk_type->name_off);
>  
> -	ret = snprintf(safe_tname, sizeof(safe_tname), "%s__safe_fields", tname);
> +	ret = snprintf(safe_tname, sizeof(safe_tname), "%s%s", tname, suffix);
>  	if (ret < 0)
>  		return false;
>  
> diff --git a/kernel/bpf/cpumask.c b/kernel/bpf/cpumask.c
> index 2b3fbbfebdc5..2223562dd54e 100644
> --- a/kernel/bpf/cpumask.c
> +++ b/kernel/bpf/cpumask.c
> @@ -427,26 +427,26 @@ BTF_ID_FLAGS(func, bpf_cpumask_create, KF_ACQUIRE | KF_RET_NULL)
>  BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE | KF_TRUSTED_ARGS)
>  BTF_ID_FLAGS(func, bpf_cpumask_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS)
>  BTF_ID_FLAGS(func, bpf_cpumask_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL)
> -BTF_ID_FLAGS(func, bpf_cpumask_first, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_first_zero, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_set_cpu, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_clear_cpu, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_test_cpu, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_test_and_set_cpu, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_test_and_clear_cpu, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_setall, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_clear, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_and, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_or, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_xor, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_equal, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_intersects, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_subset, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_full, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_copy, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_any, KF_TRUSTED_ARGS)
> -BTF_ID_FLAGS(func, bpf_cpumask_any_and, KF_TRUSTED_ARGS)
> +BTF_ID_FLAGS(func, bpf_cpumask_first, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_first_zero, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_set_cpu, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_clear_cpu, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_test_cpu, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_test_and_set_cpu, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_test_and_clear_cpu, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_setall, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_clear, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_and, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_or, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_xor, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_equal, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_intersects, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_subset, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_full, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_copy, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_any, KF_TRUSTED_ARGS | KF_RCU)
> +BTF_ID_FLAGS(func, bpf_cpumask_any_and, KF_TRUSTED_ARGS | KF_RCU)

It's functionally the same, but could you please remove KF_TRUSTED_ARGS
given that it's accepted for KF_RCU? We should ideally be removing
KF_TRUSTED_ARGS altogether soon(ish) anyways, so might as well do it
here.

>  BTF_SET8_END(cpumask_kfunc_btf_ids)
>  
>  static const struct btf_kfunc_id_set cpumask_kfunc_set = {
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a095055d7ef4..10d674e8154a 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5073,29 +5073,76 @@ static int bpf_map_direct_read(struct bpf_map *map, int off, int size, u64 *val)
>  	return 0;
>  }
>  
> -#define BTF_TYPE_SAFE_NESTED(__type)  __PASTE(__type, __safe_fields)
> +#define BTF_TYPE_SAFE_RCU(__type)  __PASTE(__type, __safe_rcu)
> +#define BTF_TYPE_SAFE_TRUSTED(__type)  __PASTE(__type, __safe_trusted)
>  
> -BTF_TYPE_SAFE_NESTED(struct task_struct) {
> +/*
> + * Allow list few fields as RCU trusted or full trusted.
> + * This logic doesn't allow mix tagging and will be removed once GCC supports
> + * btf_type_tag.
> + */
> +
> +/* RCU trusted: these fields are trusted in RCU CS and never NULL */
> +BTF_TYPE_SAFE_RCU(struct task_struct) {
>  	const cpumask_t *cpus_ptr;
>  	struct css_set __rcu *cgroups;
> +	struct task_struct __rcu *real_parent;
> +	struct task_struct *group_leader;
>  };
>  
> -BTF_TYPE_SAFE_NESTED(struct css_set) {
> +BTF_TYPE_SAFE_RCU(struct css_set) {
>  	struct cgroup *dfl_cgrp;
>  };
>  
> -static bool nested_ptr_is_trusted(struct bpf_verifier_env *env,
> -				  struct bpf_reg_state *reg,
> -				  int off)
> +/* full trusted: these fields are trusted even outside of RCU CS and never NULL */
> +BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta) {
> +	__bpf_md_ptr(struct seq_file *, seq);
> +};
> +
> +BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task) {
> +	__bpf_md_ptr(struct bpf_iter_meta *, meta);
> +	__bpf_md_ptr(struct task_struct *, task);
> +};
> +
> +BTF_TYPE_SAFE_TRUSTED(struct linux_binprm) {
> +	struct file *file;
> +};
> +
> +BTF_TYPE_SAFE_TRUSTED(struct file) {
> +	struct inode *f_inode;
> +};
> +
> +BTF_TYPE_SAFE_TRUSTED(struct dentry) {
> +	/* no negative dentry-s in places where bpf can see it */
> +	struct inode *d_inode;
> +};
> +
> +BTF_TYPE_SAFE_TRUSTED(struct socket) {
> +	struct sock *sk;
> +};
> +
> +static bool type_is_rcu(struct bpf_verifier_env *env,
> +			struct bpf_reg_state *reg,
> +			int off)
>  {
> -	/* If its parent is not trusted, it can't regain its trusted status. */
> -	if (!is_trusted_reg(reg))
> -		return false;
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct task_struct));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct css_set));
>  
> -	BTF_TYPE_EMIT(BTF_TYPE_SAFE_NESTED(struct task_struct));
> -	BTF_TYPE_EMIT(BTF_TYPE_SAFE_NESTED(struct css_set));
> +	return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_rcu");
> +}
>  
> -	return btf_nested_type_is_trusted(&env->log, reg, off);
> +static bool type_is_trusted(struct bpf_verifier_env *env,
> +			    struct bpf_reg_state *reg,
> +			    int off)
> +{
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct linux_binprm));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct file));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct dentry));
> +	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct socket));
> +
> +	return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_trusted");
>  }
>  
>  static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
> @@ -5181,49 +5228,58 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
>  	if (ret < 0)
>  		return ret;
>  
> -	/* If this is an untrusted pointer, all pointers formed by walking it
> -	 * also inherit the untrusted flag.
> -	 */
> -	if (type_flag(reg->type) & PTR_UNTRUSTED)
> -		flag |= PTR_UNTRUSTED;
> +	if (ret != PTR_TO_BTF_ID) {
> +		/* just mark; */
>  
> -	/* By default any pointer obtained from walking a trusted pointer is no
> -	 * longer trusted, unless the field being accessed has explicitly been
> -	 * marked as inheriting its parent's state of trust.
> -	 *
> -	 * An RCU-protected pointer can also be deemed trusted if we are in an
> -	 * RCU read region. This case is handled below.
> -	 */
> -	if (nested_ptr_is_trusted(env, reg, off)) {
> -		flag |= PTR_TRUSTED;
> -		/*
> -		 * task->cgroups is trusted. It provides a stronger guarantee
> -		 * than __rcu tag on 'cgroups' field in 'struct task_struct'.
> -		 * Clear MEM_RCU in such case.
> +	} else if (type_flag(reg->type) & PTR_UNTRUSTED) {
> +		/* If this is an untrusted pointer, all pointers formed by walking it
> +		 * also inherit the untrusted flag.
> +		 */
> +		flag = PTR_UNTRUSTED;
> +
> +	} else if (is_trusted_reg(reg) || is_rcu_reg(reg)) {
> +		/* By default any pointer obtained from walking a trusted pointer is no
> +		 * longer trusted, unless the field being accessed has explicitly been
> +		 * marked as inheriting its parent's state of trust (either full or RCU).
> +		 * For example:
> +		 * 'cgroups' pointer is untrusted if task->cgroups dereference
> +		 * happened in a sleepable program outside of bpf_rcu_read_lock()
> +		 * section. In a non-sleepable program it's trusted while in RCU CS (aka MEM_RCU).
> +		 * Note bpf_rcu_read_unlock() converts MEM_RCU pointers to PTR_UNTRUSTED.
> +		 *
> +		 * A regular RCU-protected pointer with __rcu tag can also be deemed
> +		 * trusted if we are in an RCU CS. Such pointer can be NULL.
>  		 */
> -		flag &= ~MEM_RCU;
> +		if (type_is_trusted(env, reg, off)) {
> +			flag |= PTR_TRUSTED;
> +		} else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) {
> +			if (type_is_rcu(env, reg, off)) {
> +				/* ignore __rcu tag and mark it MEM_RCU */
> +				flag |= MEM_RCU;
> +			} else if (flag & MEM_RCU) {
> +				/* __rcu tagged pointers can be NULL */
> +				flag |= PTR_MAYBE_NULL;

I'm not quite understanding the distinction between manually-specified
RCU-safe types being non-nullable, vs. __rcu pointers being nullable.
Aren't they functionally the exact same thing, with the exception being
that gcc doesn't support __rcu, so we've decided to instead manually
specify them for some types that we know we need until __rcu is the
default mechanism?  If the plan is to remove these macros once gcc
supports __rcu, this could break some programs that are expecting the
fields to be non-NULL, no?

I see why we're doing this in the interim -- task->cgroups,
css->dfl_cgrp, task->cpus_ptr, etc can never be NULL. The problem is
that I think those are implementation details that are separate from the
pointers being RCU safe. This seems rather like we need a separate
non-nullable tag, or something to that effect.

> +			} else if (flag & (MEM_PERCPU | MEM_USER)) {
> +				/* keep as-is */
> +			} else {
> +				/* walking unknown pointers yields untrusted pointer */
> +				flag = PTR_UNTRUSTED;
> +			}
> +		} else {
> +			/*
> +			 * If not in RCU CS or MEM_RCU pointer can be NULL then
> +			 * aggressively mark as untrusted otherwise such
> +			 * pointers will be plain PTR_TO_BTF_ID without flags
> +			 * and will be allowed to be passed into helpers for
> +			 * compat reasons.
> +			 */
> +			flag = PTR_UNTRUSTED;
> +		}
>  	} else {
> +		/* Old compat. Deperecated */

s/Deperecated/Deprecated

>  		flag &= ~PTR_TRUSTED;

Do you know what else is left for us to fix to be able to just set
PTR_UNTRUSTED here?

>  	}
>  
> -	if (flag & MEM_RCU) {
> -		/* Mark value register as MEM_RCU only if it is protected by
> -		 * bpf_rcu_read_lock() and the ptr reg is rcu or trusted. MEM_RCU
> -		 * itself can already indicate trustedness inside the rcu
> -		 * read lock region. Also mark rcu pointer as PTR_MAYBE_NULL since
> -		 * it could be null in some cases.
> -		 */
> -		if (in_rcu_cs(env) && (is_trusted_reg(reg) || is_rcu_reg(reg)))
> -			flag |= PTR_MAYBE_NULL;
> -		else
> -			flag &= ~MEM_RCU;
> -	} else if (reg->type & MEM_RCU) {
> -		/* ptr (reg) is marked as MEM_RCU, but the struct field is not tagged
> -		 * with __rcu. Mark the flag as PTR_UNTRUSTED conservatively.
> -		 */
> -		flag |= PTR_UNTRUSTED;
> -	}
> -
>  	if (atype == BPF_READ && value_regno >= 0)
>  		mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
>  
> @@ -10049,10 +10105,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>  
>  	rcu_lock = is_kfunc_bpf_rcu_read_lock(&meta);
>  	rcu_unlock = is_kfunc_bpf_rcu_read_unlock(&meta);
> -	if ((rcu_lock || rcu_unlock) && !env->rcu_tag_supported) {
> -		verbose(env, "no vmlinux btf rcu tag support for kfunc %s\n", func_name);
> -		return -EACCES;
> -	}
>  
>  	if (env->cur_state->active_rcu_lock) {
>  		struct bpf_func_state *state;
> @@ -14911,8 +14963,22 @@ static int do_check(struct bpf_verifier_env *env)
>  				 * src_reg == stack|map in some other branch.
>  				 * Reject it.
>  				 */
> -				verbose(env, "same insn cannot be used with different pointers\n");
> -				return -EINVAL;
> +				if (base_type(src_reg_type) == PTR_TO_BTF_ID &&
> +				    base_type(*prev_src_type) == PTR_TO_BTF_ID) {
> +					/*
> +					 * Have to support a use case when one path through
> +					 * the program yields TRUSTED pointer while another
> +					 * is UNTRUSTED. Fallback to UNTRUSTED to generate
> +					 * BPF_PROBE_MEM.
> +					 */
> +					*prev_src_type = PTR_TO_BTF_ID | PTR_UNTRUSTED;
> +				} else {
> +					verbose(env,
> +						"The same insn cannot be used with different pointers: %s",
> +						reg_type_str(env, src_reg_type));
> +					verbose(env, " != %s\n", reg_type_str(env, *prev_src_type));
> +					return -EINVAL;
> +				}
>  			}
>  
>  		} else if (class == BPF_STX) {
> @@ -17984,8 +18050,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr)
>  	env->bypass_spec_v1 = bpf_bypass_spec_v1();
>  	env->bypass_spec_v4 = bpf_bypass_spec_v4();
>  	env->bpf_capable = bpf_capable();
> -	env->rcu_tag_supported = btf_vmlinux &&
> -		btf_find_by_name_kind(btf_vmlinux, "rcu", BTF_KIND_TYPE_TAG) > 0;
>  
>  	if (is_priv)
>  		env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ;
> diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c
> index 2cc759956e3b..63e776f4176e 100644
> --- a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c
> +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c
> @@ -193,7 +193,7 @@ static void test_cgroup_iter_sleepable(int cgroup_fd, __u64 cgroup_id)
>  	cgrp_ls_sleepable__destroy(skel);
>  }
>  
> -static void test_no_rcu_lock(__u64 cgroup_id)
> +static void test_yes_rcu_lock(__u64 cgroup_id)
>  {
>  	struct cgrp_ls_sleepable *skel;
>  	int err;
> @@ -204,7 +204,7 @@ static void test_no_rcu_lock(__u64 cgroup_id)
>  
>  	skel->bss->target_pid = syscall(SYS_gettid);
>  
> -	bpf_program__set_autoload(skel->progs.no_rcu_lock, true);
> +	bpf_program__set_autoload(skel->progs.yes_rcu_lock, true);
>  	err = cgrp_ls_sleepable__load(skel);
>  	if (!ASSERT_OK(err, "skel_load"))
>  		goto out;
> @@ -220,7 +220,7 @@ static void test_no_rcu_lock(__u64 cgroup_id)
>  	cgrp_ls_sleepable__destroy(skel);
>  }
>  
> -static void test_rcu_lock(void)
> +static void test_no_rcu_lock(void)
>  {
>  	struct cgrp_ls_sleepable *skel;
>  	int err;
> @@ -229,7 +229,7 @@ static void test_rcu_lock(void)
>  	if (!ASSERT_OK_PTR(skel, "skel_open"))
>  		return;
>  
> -	bpf_program__set_autoload(skel->progs.yes_rcu_lock, true);
> +	bpf_program__set_autoload(skel->progs.no_rcu_lock, true);
>  	err = cgrp_ls_sleepable__load(skel);
>  	ASSERT_ERR(err, "skel_load");
>  
> @@ -256,10 +256,10 @@ void test_cgrp_local_storage(void)
>  		test_negative();
>  	if (test__start_subtest("cgroup_iter_sleepable"))
>  		test_cgroup_iter_sleepable(cgroup_fd, cgroup_id);
> +	if (test__start_subtest("yes_rcu_lock"))
> +		test_yes_rcu_lock(cgroup_id);
>  	if (test__start_subtest("no_rcu_lock"))
> -		test_no_rcu_lock(cgroup_id);
> -	if (test__start_subtest("rcu_lock"))
> -		test_rcu_lock();
> +		test_no_rcu_lock();
>  
>  	close(cgroup_fd);
>  }
> diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
> index 447d8560ecb6..3f1f58d3a729 100644
> --- a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
> +++ b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
> @@ -25,10 +25,10 @@ static void test_success(void)
>  
>  	bpf_program__set_autoload(skel->progs.get_cgroup_id, true);
>  	bpf_program__set_autoload(skel->progs.task_succ, true);
> -	bpf_program__set_autoload(skel->progs.no_lock, true);
>  	bpf_program__set_autoload(skel->progs.two_regions, true);
>  	bpf_program__set_autoload(skel->progs.non_sleepable_1, true);
>  	bpf_program__set_autoload(skel->progs.non_sleepable_2, true);
> +	bpf_program__set_autoload(skel->progs.task_trusted_non_rcuptr, true);
>  	err = rcu_read_lock__load(skel);
>  	if (!ASSERT_OK(err, "skel_load"))
>  		goto out;
> @@ -69,6 +69,7 @@ static void test_rcuptr_acquire(void)
>  
>  static const char * const inproper_region_tests[] = {
>  	"miss_lock",
> +	"no_lock",
>  	"miss_unlock",
>  	"non_sleepable_rcu_mismatch",
>  	"inproper_sleepable_helper",
> @@ -99,7 +100,6 @@ static void test_inproper_region(void)
>  }
>  
>  static const char * const rcuptr_misuse_tests[] = {
> -	"task_untrusted_non_rcuptr",
>  	"task_untrusted_rcuptr",
>  	"cross_rcu_region",
>  };
> @@ -128,17 +128,8 @@ static void test_rcuptr_misuse(void)
>  
>  void test_rcu_read_lock(void)
>  {
> -	struct btf *vmlinux_btf;
>  	int cgroup_fd;
>  
> -	vmlinux_btf = btf__load_vmlinux_btf();
> -	if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF"))
> -		return;
> -	if (btf__find_by_name_kind(vmlinux_btf, "rcu", BTF_KIND_TYPE_TAG) < 0) {
> -		test__skip();
> -		goto out;
> -	}
> -
>  	cgroup_fd = test__join_cgroup("/rcu_read_lock");
>  	if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /rcu_read_lock"))
>  		goto out;
> @@ -153,6 +144,5 @@ void test_rcu_read_lock(void)
>  	if (test__start_subtest("negative_tests_rcuptr_misuse"))
>  		test_rcuptr_misuse();
>  	close(cgroup_fd);
> -out:
> -	btf__free(vmlinux_btf);
> +out:;
>  }
> diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c b/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c
> index 2d11ed528b6f..7615dc23d301 100644
> --- a/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c
> +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c
> @@ -49,7 +49,7 @@ int no_rcu_lock(void *ctx)
>  	if (task->pid != target_pid)
>  		return 0;
>  
> -	/* ptr_to_btf_id semantics. should work. */
> +	/* task->cgroups is untrusted in sleepable prog outside of RCU CS */
>  	cgrp = task->cgroups->dfl_cgrp;
>  	ptr = bpf_cgrp_storage_get(&map_a, cgrp, 0,
>  				   BPF_LOCAL_STORAGE_GET_F_CREATE);
> @@ -71,7 +71,7 @@ int yes_rcu_lock(void *ctx)
>  
>  	bpf_rcu_read_lock();
>  	cgrp = task->cgroups->dfl_cgrp;
> -	/* cgrp is untrusted and cannot pass to bpf_cgrp_storage_get() helper. */
> +	/* cgrp is trusted under RCU CS */
>  	ptr = bpf_cgrp_storage_get(&map_a, cgrp, 0, BPF_LOCAL_STORAGE_GET_F_CREATE);
>  	if (ptr)
>  		cgroup_id = cgrp->kn->id;
> diff --git a/tools/testing/selftests/bpf/progs/cpumask_failure.c b/tools/testing/selftests/bpf/progs/cpumask_failure.c
> index 33e8e86dd090..c16f7563b84e 100644
> --- a/tools/testing/selftests/bpf/progs/cpumask_failure.c
> +++ b/tools/testing/selftests/bpf/progs/cpumask_failure.c
> @@ -44,7 +44,7 @@ int BPF_PROG(test_alloc_double_release, struct task_struct *task, u64 clone_flag
>  }
>  
>  SEC("tp_btf/task_newtask")
> -__failure __msg("bpf_cpumask_acquire args#0 expected pointer to STRUCT bpf_cpumask")
> +__failure __msg("must be referenced")
>  int BPF_PROG(test_acquire_wrong_cpumask, struct task_struct *task, u64 clone_flags)
>  {
>  	struct bpf_cpumask *cpumask;
> diff --git a/tools/testing/selftests/bpf/progs/nested_trust_failure.c b/tools/testing/selftests/bpf/progs/nested_trust_failure.c
> index 14aff7676436..0d1aa6bbace4 100644
> --- a/tools/testing/selftests/bpf/progs/nested_trust_failure.c
> +++ b/tools/testing/selftests/bpf/progs/nested_trust_failure.c
> @@ -17,7 +17,7 @@ char _license[] SEC("license") = "GPL";
>   */
>  
>  SEC("tp_btf/task_newtask")
> -__failure __msg("R2 must be referenced or trusted")
> +__failure __msg("R2 must be")
>  int BPF_PROG(test_invalid_nested_user_cpus, struct task_struct *task, u64 clone_flags)
>  {
>  	bpf_cpumask_test_cpu(0, task->user_cpus_ptr);
> diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c
> index 5cecbdbbb16e..7250bb76d18a 100644
> --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c
> +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c
> @@ -81,7 +81,7 @@ int no_lock(void *ctx)
>  {
>  	struct task_struct *task, *real_parent;
>  
> -	/* no bpf_rcu_read_lock(), old code still works */
> +	/* old style ptr_to_btf_id is not allowed in sleepable */
>  	task = bpf_get_current_task_btf();
>  	real_parent = task->real_parent;
>  	(void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
> @@ -286,13 +286,13 @@ int nested_rcu_region(void *ctx)
>  }
>  
>  SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
> -int task_untrusted_non_rcuptr(void *ctx)
> +int task_trusted_non_rcuptr(void *ctx)
>  {
>  	struct task_struct *task, *group_leader;
>  
>  	task = bpf_get_current_task_btf();
>  	bpf_rcu_read_lock();
> -	/* the pointer group_leader marked as untrusted */
> +	/* the pointer group_leader is explicitly marked as trusted */
>  	group_leader = task->real_parent->group_leader;
>  	(void)bpf_task_storage_get(&map_a, group_leader, 0, 0);
>  	bpf_rcu_read_unlock();
> diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
> index 9a326a800e5c..5702fc9761ef 100644
> --- a/tools/testing/selftests/bpf/verifier/calls.c
> +++ b/tools/testing/selftests/bpf/verifier/calls.c
> @@ -181,7 +181,7 @@
>  	},
>  	.result_unpriv = REJECT,
>  	.result = REJECT,
> -	.errstr = "negative offset ptr_ ptr R1 off=-4 disallowed",
> +	.errstr = "ptr R1 off=-4 disallowed",
>  },
>  {
>  	"calls: invalid kfunc call: PTR_TO_BTF_ID with variable offset",
> -- 
> 2.39.2
> 

  reply	other threads:[~2023-03-02  4:05 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-01 22:35 [PATCH v4 bpf-next 0/6] bpf: Introduce kptr RCU Alexei Starovoitov
2023-03-01 22:35 ` [PATCH v4 bpf-next 1/6] bpf: Rename __kptr_ref -> __kptr and __kptr -> __kptr_untrusted Alexei Starovoitov
2023-03-01 22:35 ` [PATCH v4 bpf-next 2/6] bpf: Mark cgroups and dfl_cgrp fields as trusted Alexei Starovoitov
2023-03-01 22:35 ` [PATCH v4 bpf-next 3/6] bpf: Introduce kptr_rcu Alexei Starovoitov
2023-03-02  0:29   ` David Vernet
2023-03-01 22:35 ` [PATCH v4 bpf-next 4/6] selftests/bpf: Add a test case for kptr_rcu Alexei Starovoitov
2023-03-01 22:35 ` [PATCH v4 bpf-next 5/6] selftests/bpf: Tweak cgroup kfunc test Alexei Starovoitov
2023-03-02  0:32   ` David Vernet
2023-03-01 22:35 ` [PATCH v4 bpf-next 6/6] bpf: Refactor RCU enforcement in the verifier Alexei Starovoitov
2023-03-02  4:05   ` David Vernet [this message]
2023-03-02 21:23     ` Alexei Starovoitov
2023-03-02 23:03       ` David Vernet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZAAgfwgo5GU8V28f@maniforge \
    --to=void@manifault.com \
    --cc=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=davemarchevsky@meta.com \
    --cc=kernel-team@fb.com \
    --cc=martin.lau@kernel.org \
    --cc=memxor@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.