Commit e2b3c4ff authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov
Browse files

bpf: add __arg_trusted global func arg tag



Add support for passing PTR_TO_BTF_ID registers to global subprogs.
Currently only PTR_TRUSTED flavor of PTR_TO_BTF_ID is supported.
Non-NULL semantics is assumed, so caller will be forced to prove
PTR_TO_BTF_ID can't be NULL.

Note, we disallow global subprogs to destroy passed in PTR_TO_BTF_ID
arguments, even the trusted one. We achieve that by not setting
ref_obj_id when validating subprog code. This basically enforces (in
Rust terms) borrowing semantics vs move semantics. Borrowing semantics
seems to be a better fit for isolated global subprog validation
approach.

Implementation-wise, we utilize existing logic for matching
user-provided BTF type to kernel-side BTF type, used by BPF CO-RE logic
and following same matching rules. We enforce a unique match for types.

Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20240130000648.2144827-2-andrii@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 24219056
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -610,6 +610,7 @@ struct bpf_subprog_arg_info {
	enum bpf_arg_type arg_type;
	union {
		u32 mem_size;
		u32 btf_id;
	};
};

+86 −13
Original line number Diff line number Diff line
@@ -6985,9 +6985,77 @@ static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t)
	return false;
}

struct bpf_cand_cache {
	const char *name;
	u32 name_len;
	u16 kind;
	u16 cnt;
	struct {
		const struct btf *btf;
		u32 id;
	} cands[];
};

static DEFINE_MUTEX(cand_cache_mutex);

static struct bpf_cand_cache *
bpf_core_find_cands(struct bpf_core_ctx *ctx, u32 local_type_id);

static int btf_get_ptr_to_btf_id(struct bpf_verifier_log *log, int arg_idx,
				 const struct btf *btf, const struct btf_type *t)
{
	struct bpf_cand_cache *cc;
	struct bpf_core_ctx ctx = {
		.btf = btf,
		.log = log,
	};
	u32 kern_type_id, type_id;
	int err = 0;

	/* skip PTR and modifiers */
	type_id = t->type;
	t = btf_type_by_id(btf, t->type);
	while (btf_type_is_modifier(t)) {
		type_id = t->type;
		t = btf_type_by_id(btf, t->type);
	}

	mutex_lock(&cand_cache_mutex);
	cc = bpf_core_find_cands(&ctx, type_id);
	if (IS_ERR(cc)) {
		err = PTR_ERR(cc);
		bpf_log(log, "arg#%d reference type('%s %s') candidate matching error: %d\n",
			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off),
			err);
		goto cand_cache_unlock;
	}
	if (cc->cnt != 1) {
		bpf_log(log, "arg#%d reference type('%s %s') %s\n",
			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off),
			cc->cnt == 0 ? "has no matches" : "is ambiguous");
		err = cc->cnt == 0 ? -ENOENT : -ESRCH;
		goto cand_cache_unlock;
	}
	if (btf_is_module(cc->cands[0].btf)) {
		bpf_log(log, "arg#%d reference type('%s %s') points to kernel module type (unsupported)\n",
			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off));
		err = -EOPNOTSUPP;
		goto cand_cache_unlock;
	}
	kern_type_id = cc->cands[0].id;

cand_cache_unlock:
	mutex_unlock(&cand_cache_mutex);
	if (err)
		return err;

	return kern_type_id;
}

enum btf_arg_tag {
	ARG_TAG_CTX = 0x1,
	ARG_TAG_NONNULL = 0x2,
	ARG_TAG_TRUSTED = 0x4,
};

/* Process BTF of a function to produce high-level expectation of function
@@ -7089,6 +7157,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)

			if (strcmp(tag, "ctx") == 0) {
				tags |= ARG_TAG_CTX;
			} else if (strcmp(tag, "trusted") == 0) {
				tags |= ARG_TAG_TRUSTED;
			} else if (strcmp(tag, "nonnull") == 0) {
				tags |= ARG_TAG_NONNULL;
			} else {
@@ -7127,6 +7197,22 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
			sub->args[i].arg_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY;
			continue;
		}
		if (tags & ARG_TAG_TRUSTED) {
			int kern_type_id;

			if (tags & ARG_TAG_NONNULL) {
				bpf_log(log, "arg#%d has invalid combination of tags\n", i);
				return -EINVAL;
			}

			kern_type_id = btf_get_ptr_to_btf_id(log, i, btf, t);
			if (kern_type_id < 0)
				return kern_type_id;

			sub->args[i].arg_type = ARG_PTR_TO_BTF_ID | PTR_TRUSTED;
			sub->args[i].btf_id = kern_type_id;
			continue;
		}
		if (is_global) { /* generic user data pointer */
			u32 mem_size;

@@ -8229,17 +8315,6 @@ size_t bpf_core_essential_name_len(const char *name)
	return n;
}

struct bpf_cand_cache {
	const char *name;
	u32 name_len;
	u16 kind;
	u16 cnt;
	struct {
		const struct btf *btf;
		u32 id;
	} cands[];
};

static void bpf_free_cands(struct bpf_cand_cache *cands)
{
	if (!cands->cnt)
@@ -8260,8 +8335,6 @@ static struct bpf_cand_cache *vmlinux_cand_cache[VMLINUX_CAND_CACHE_SIZE];
#define MODULE_CAND_CACHE_SIZE 31
static struct bpf_cand_cache *module_cand_cache[MODULE_CAND_CACHE_SIZE];

static DEFINE_MUTEX(cand_cache_mutex);

static void __print_cand_cache(struct bpf_verifier_log *log,
			       struct bpf_cand_cache **cache,
			       int cache_size)
+24 −0
Original line number Diff line number Diff line
@@ -9336,6 +9336,18 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
			ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0);
			if (ret)
				return ret;
		} else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
			struct bpf_call_arg_meta meta;
			int err;
			if (register_is_null(reg) && type_may_be_null(arg->arg_type))
				continue;
			memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */
			err = check_reg_type(env, regno, arg->arg_type, &arg->btf_id, &meta);
			err = err ?: check_func_arg_reg_off(env, reg, regno, arg->arg_type);
			if (err)
				return err;
		} else {
			bpf_log(log, "verifier bug: unrecognized arg#%d type %d\n",
				i, arg->arg_type);
@@ -20137,6 +20149,18 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
				mark_reg_known_zero(env, regs, i);
				reg->mem_size = arg->mem_size;
				reg->id = ++env->id_gen;
			} else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
				reg->type = PTR_TO_BTF_ID;
				if (arg->arg_type & PTR_MAYBE_NULL)
					reg->type |= PTR_MAYBE_NULL;
				if (arg->arg_type & PTR_UNTRUSTED)
					reg->type |= PTR_UNTRUSTED;
				if (arg->arg_type & PTR_TRUSTED)
					reg->type |= PTR_TRUSTED;
				mark_reg_known_zero(env, regs, i);
				reg->btf = bpf_get_btf_vmlinux(); /* can't fail at this point */
				reg->btf_id = arg->btf_id;
				reg->id = ++env->id_gen;
			} else {
				WARN_ONCE(1, "BUG: unhandled arg#%d type %d\n",
					  i - BPF_REG_1, arg->arg_type);