Commit 4d8ebe13 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'trusted-ptr_to_btf_id-arg-support-in-global-subprogs'

Andrii Nakryiko says:

====================
Trusted PTR_TO_BTF_ID arg support in global subprogs

This patch set follows recent changes that added btf_decl_tag-based argument
annotation support for global subprogs. This time we add ability to pass
PTR_TO_BTF_ID (BTF-aware kernel pointers) arguments into global subprograms.
We support explicitly trusted arguments only, for now.

Patch #1 adds logic for arg:trusted tag support on the verifier side. Default
semantic of such arguments is non-NULL, enforced on caller side. But patch #2
adds arg:nullable tag that can be combined with arg:trusted to make callee
explicitly do the NULL check, which helps implement "optional" PTR_TO_BTF_ID
arguments.

Patch #3 adds libbpf-side __arg_trusted and __arg_nullable macros.

Patch #4 adds a bunch of tests validating __arg_trusted in combination with
__arg_nullable.

v2->v3:
  - went back to arg:nullable and __arg_nullable naming;
  - rebased on latest bpf-next after prepartory patches landed;
v1->v2:
  - added fix up to type enforcement changes, landed earlier;
  - dropped bpf_core_cast() changes, will post them separately, as they now
    are not used in added tests;
  - dropped arg:untrusted support (Alexei);
  - renamed arg:nullable to arg:maybe_null (Alexei);
  - and also added task_struct___local flavor tests (Alexei).
====================

Link: https://lore.kernel.org/r/20240130000648.2144827-1-andrii@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 24219056 c381203e
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;
	};
};

+96 −13
Original line number Diff line number Diff line
@@ -6985,9 +6985,78 @@ 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,
	ARG_TAG_NULLABLE = 0x8,
};

/* Process BTF of a function to produce high-level expectation of function
@@ -7089,8 +7158,12 @@ 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 if (strcmp(tag, "nullable") == 0) {
				tags |= ARG_TAG_NULLABLE;
			} else {
				bpf_log(log, "arg#%d has unsupported set of tags\n", i);
				return -EOPNOTSUPP;
@@ -7127,9 +7200,32 @@ 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;
			if (tags & ARG_TAG_NULLABLE)
				sub->args[i].arg_type |= PTR_MAYBE_NULL;
			sub->args[i].btf_id = kern_type_id;
			continue;
		}
		if (is_global) { /* generic user data pointer */
			u32 mem_size;

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

			t = btf_type_skip_modifiers(btf, t->type, NULL);
			ref_t = btf_resolve_size(btf, t, &mem_size);
			if (IS_ERR(ref_t)) {
@@ -8229,17 +8325,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 +8345,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);
+2 −0
Original line number Diff line number Diff line
@@ -190,6 +190,8 @@ enum libbpf_tristate {

#define __arg_ctx __attribute__((btf_decl_tag("arg:ctx")))
#define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull")))
#define __arg_nullable __attribute((btf_decl_tag("arg:nullable")))
#define __arg_trusted __attribute((btf_decl_tag("arg:trusted")))

#ifndef ___bpf_concat
#define ___bpf_concat(a, b) a ## b
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "verifier_div0.skel.h"
#include "verifier_div_overflow.skel.h"
#include "verifier_global_subprogs.skel.h"
#include "verifier_global_ptr_args.skel.h"
#include "verifier_gotol.skel.h"
#include "verifier_helper_access_var_len.skel.h"
#include "verifier_helper_packet_access.skel.h"
@@ -140,6 +141,7 @@ void test_verifier_direct_stack_access_wraparound(void) { RUN(verifier_direct_st
void test_verifier_div0(void)                 { RUN(verifier_div0); }
void test_verifier_div_overflow(void)         { RUN(verifier_div_overflow); }
void test_verifier_global_subprogs(void)      { RUN(verifier_global_subprogs); }
void test_verifier_global_ptr_args(void)      { RUN(verifier_global_ptr_args); }
void test_verifier_gotol(void)                { RUN(verifier_gotol); }
void test_verifier_helper_access_var_len(void) { RUN(verifier_helper_access_var_len); }
void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_access); }
Loading