All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jay Wang <wanjay@amazon.com>
To: <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Ihor Solodrai <ihor.solodrai@linux.dev>,
	Alan Maguire <almagui@kernel.org>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	Steven Rostedt <rostedt@goodmis.org>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
	<linux-trace-kernel@vger.kernel.org>,
	Nathan Chancellor <nathan@kernel.org>,
	Nicolas Schier <nsc@kernel.org>, <linux-kbuild@vger.kernel.org>,
	"Arnd Bergmann" <arnd@arndb.de>, <linux-arch@vger.kernel.org>,
	<x86@kernel.org>, <linux-doc@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	"Hazem Mohamed Abuelfotoh" <abuehaze@amazon.com>,
	Bjoern Doebel <doebel@amazon.de>,
	"Martin Pohlack" <mpohlack@amazon.de>,
	<jay.wang.upstream@gmail.com>
Subject: [PATCH bpf-next 01/13] bpf: match program context types by name without vmlinux BTF
Date: Sat, 31 Oct 2026 13:09:47 +0000	[thread overview]
Message-ID: <20261031130959.23472-2-wanjay@amazon.com> (raw)
In-Reply-To: <20261031130959.23472-1-wanjay@amazon.com>

From: Ihor Solodrai <ihor.solodrai@linux.dev>

btf_is_prog_ctx_type() and btf_validate_prog_ctx_type() decide whether
a BTF function argument is the program's context.  They look up the
canonical context type of the program type in bpf_ctx_convert, a type
that only exists in the vmlinux BTF, but then use nothing but its name:
the argument's type name is compared with it, with the __sk_buff ->
sk_buff and xdp_md -> xdp_buff projections, and with one typedef
resolution step for bpf_user_pt_regs_t.

btf_prepare_func_args() runs this check for the main program of every
load that carries func_info (e.g. the socket filter and cgroup programs
systemd loads at boot) and for subprograms.  Once the vmlinux BTF is
parsed on first use (CONFIG_DEBUG_INFO_BTF_LAZY, later in this series),
fetching the BTF here would make all of them pay for parsing it, and
skipping the check when the BTF is not there yet would make the
verification result (e.g. func_info "unreliable") depend on whether
something else parsed the BTF first.

Take the names from include/linux/bpf_types.h instead: a table indexed
by program type is built from the prog_ctx_type argument of
BPF_PROG_TYPE(), and prog_ctx_tname() returns the struct tag, or the
typedef name.  For bpf_user_pt_regs_t it also returns the name of the
underlying struct, found with __builtin_types_compatible_p() the same
way btf_validate_prog_ctx_type() already does for perf_event programs.
The other forms are equivalent to what the BTF lookup produced:

 - typedefs of integers (__u64, __u32): the old code compared the
   typedef name and then the integer type name, which never equals a
   struct tag; only the typedef name is compared now;
 - "void *" (tracing, struct_ops, ext, lsm, syscall): no named context
   type, as before;
 - s390 typedefs bpf_user_pt_regs_t to an anonymous struct: nothing
   matches, as before; only the __arg_ctx error message changes from
   "should be `struct  *`" to "should be void pointer";
 - program types that are not configured in used to index
   bpf_ctx_convert_map[] with 0 (socket filter); they now get no name.
   Such programs cannot be loaded anyway.

find_kern_ctx_type_id() and get_kern_ctx_btf_id() still need the kernel
context type (tracing/EXT access to a BPF program's context argument,
bpf_cast_to_kern_ctx()).  Kernel types enter the program there, so they
now fetch the vmlinux BTF with bpf_get_btf_vmlinux() instead of relying
on bpf_check() having done it.

No change with CONFIG_DEBUG_INFO_BTF=y.  With CONFIG_DEBUG_INFO_BTF=n,
global subprograms taking e.g. a struct __sk_buff *ctx now verify
instead of failing with "btf_vmlinux is malformed", as they do with =y.

TODO-NEEDS-IHOR: Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
[split the BPF_PROG_TYPE() definition over two lines]
Signed-off-by: Jay Wang <wanjay@amazon.com>
---
 kernel/bpf/btf.c | 116 ++++++++++++++++++++++-------------------------
 1 file changed, 53 insertions(+), 63 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 94d43612d211..1c7536a22a63 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -6557,20 +6557,46 @@ static u8 bpf_ctx_convert_map[] = {
 #undef BPF_MAP_TYPE
 #undef BPF_LINK_TYPE
 
-static const struct btf_type *find_canonical_prog_ctx_type(enum bpf_prog_type prog_type)
+#define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
+#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
+	[_id] = #prog_ctx_type,
+static const char * const bpf_prog_ctx_tname[] = {
+#include <linux/bpf_types.h>
+};
+#undef BPF_PROG_TYPE
+#undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
+
+struct user_regs_struct;
+struct user_pt_regs;
+
+/*
+ * Name of the context struct of @prog_type, as written in bpf_types.h, or NULL
+ * if it has none ("void *").  Only names are compared against the program's
+ * BTF, so the vmlinux BTF is not needed.  *@alt is set to the struct behind
+ * the bpf_user_pt_regs_t typedef.  Typedefs of integers (__u64, __u32) never
+ * match a struct name.
+ */
+static const char *prog_ctx_tname(enum bpf_prog_type prog_type, const char **alt)
 {
-	const struct btf_type *conv_struct;
-	const struct btf_member *ctx_type;
+	const char *name = prog_type < ARRAY_SIZE(bpf_prog_ctx_tname) ?
+			   bpf_prog_ctx_tname[prog_type] : NULL;
 
-	conv_struct = bpf_ctx_convert.t;
-	if (!conv_struct)
+	*alt = NULL;
+	if (!name || strchr(name, '*'))
 		return NULL;
-	/* prog_type is valid bpf program type. No need for bounds check. */
-	ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2;
-	/* ctx_type is a pointer to prog_ctx_type in vmlinux.
-	 * Like 'struct __sk_buff'
-	 */
-	return btf_type_by_id(btf_vmlinux, ctx_type->type);
+	if (str_has_prefix(name, "struct "))
+		return name + strlen("struct ");
+	if (!strcmp(name, "bpf_user_pt_regs_t")) {
+		if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct pt_regs))
+			*alt = "pt_regs";
+		else if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct user_pt_regs))
+			*alt = "user_pt_regs";
+		else if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct user_regs_struct))
+			*alt = "user_regs_struct";
+	}
+	return name;
 }
 
 static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)
@@ -6578,9 +6604,10 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)
 	const struct btf_type *conv_struct;
 	const struct btf_member *ctx_type;
 
-	conv_struct = bpf_ctx_convert.t;
-	if (!conv_struct)
+	/* bpf_ctx_convert.t is set when the vmlinux BTF is parsed */
+	if (IS_ERR_OR_NULL(bpf_get_btf_vmlinux()))
 		return -EFAULT;
+	conv_struct = bpf_ctx_convert.t;
 	/* prog_type is valid bpf program type. No need for bounds check. */
 	ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2 + 1;
 	/* ctx_type is a pointer to prog_ctx_type in vmlinux.
@@ -6602,8 +6629,7 @@ bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
 			  const struct btf_type *t, enum bpf_prog_type prog_type,
 			  int arg)
 {
-	const struct btf_type *ctx_type;
-	const char *tname, *ctx_tname;
+	const char *tname, *ctx_tname, *alt;
 
 	t = btf_type_by_id(btf, t->type);
 
@@ -6637,21 +6663,9 @@ bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
 		return false;
 	}
 
-	ctx_type = find_canonical_prog_ctx_type(prog_type);
-	if (!ctx_type) {
-		bpf_log(log, "btf_vmlinux is malformed\n");
-		/* should not happen */
-		return false;
-	}
-again:
-	ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
-	if (!ctx_tname) {
-		/* should not happen */
-		bpf_log(log, "Please fix kernel include/linux/bpf_types.h\n");
-		return false;
-	}
 	/* program types without named context types work only with arg:ctx tag */
-	if (ctx_tname[0] == '\0')
+	ctx_tname = prog_ctx_tname(prog_type, &alt);
+	if (!ctx_tname)
 		return false;
 	/* only compare that prog's ctx type name is the same as
 	 * kernel expects. No need to compare field by field.
@@ -6660,37 +6674,18 @@ bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
 	 * int socket_filter_bpf_prog(struct __sk_buff *skb)
 	 * { // no fields of skb are ever used }
 	 */
-	if (btf_is_projection_of(ctx_tname, tname))
+	if (btf_is_projection_of(ctx_tname, tname) || !strcmp(ctx_tname, tname))
 		return true;
-	if (strcmp(ctx_tname, tname)) {
-		/* bpf_user_pt_regs_t is a typedef, so resolve it to
-		 * underlying struct and check name again
-		 */
-		if (!btf_type_is_modifier(ctx_type))
-			return false;
-		while (btf_type_is_modifier(ctx_type))
-			ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);
-		goto again;
-	}
-	return true;
+	/* bpf_user_pt_regs_t is a typedef, check the underlying struct too */
+	return alt && !strcmp(alt, tname);
 }
 
-/* forward declarations for arch-specific underlying types of
- * bpf_user_pt_regs_t; this avoids the need for arch-specific #ifdef
- * compilation guards below for BPF_PROG_TYPE_PERF_EVENT checks, but still
- * works correctly with __builtin_types_compatible_p() on respective
- * architectures
- */
-struct user_regs_struct;
-struct user_pt_regs;
-
 static int btf_validate_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
 				      const struct btf_type *t, int arg,
 				      enum bpf_prog_type prog_type,
 				      enum bpf_attach_type attach_type)
 {
-	const struct btf_type *ctx_type;
-	const char *tname, *ctx_tname;
+	const char *tname, *ctx_tname, *alt;
 
 	if (!btf_is_ptr(t)) {
 		bpf_log(log, "arg#%d type isn't a pointer\n", arg);
@@ -6790,27 +6785,20 @@ static int btf_validate_prog_ctx_type(struct bpf_verifier_log *log, const struct
 		break;
 	}
 
-	ctx_type = find_canonical_prog_ctx_type(prog_type);
-	if (!ctx_type) {
-		/* should not happen */
-		bpf_log(log, "btf_vmlinux is malformed\n");
-		return -EINVAL;
-	}
-
 	/* resolve typedefs and check that underlying structs are matching as well */
-	while (btf_type_is_modifier(ctx_type))
-		ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);
+	ctx_tname = prog_ctx_tname(prog_type, &alt);
+	if (ctx_tname && !str_has_prefix(bpf_prog_ctx_tname[prog_type], "struct "))
+		ctx_tname = alt;
 
 	/* if program type doesn't have distinctly named struct type for
 	 * context, then __arg_ctx argument can only be `void *`, which we
 	 * already checked above
 	 */
-	if (!__btf_type_is_struct(ctx_type)) {
+	if (!ctx_tname) {
 		bpf_log(log, "arg#%d should be void pointer\n", arg);
 		return -EINVAL;
 	}
 
-	ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
 	if (!__btf_type_is_struct(t) || strcmp(ctx_tname, tname) != 0) {
 		bpf_log(log, "arg#%d should be `struct %s *`\n", arg, ctx_tname);
 		return -EINVAL;
@@ -6837,6 +6825,8 @@ int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_ty
 	const struct btf_type *kctx_type;
 	u32 kctx_type_id;
 
+	if (IS_ERR_OR_NULL(bpf_get_btf_vmlinux()))
+		return -EINVAL;
 	conv_struct = bpf_ctx_convert.t;
 	/* get member for kernel ctx type */
 	kctx_member = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2 + 1;
-- 
2.47.3


  reply	other threads:[~2026-10-31 13:10 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31 13:09 [PATCH bpf-next 00/13] bpf: keep the vmlinux BTF compressed until first use (CONFIG_DEBUG_INFO_BTF_LAZY) to save ~6 MB memory Jay Wang
2026-10-31 13:09 ` Jay Wang [this message]
2026-10-31 13:43   ` [PATCH bpf-next 01/13] bpf: match program context types by name without vmlinux BTF bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 02/13] bpf: fetch the vmlinux BTF where kernel types enter a program Jay Wang
2026-10-31 13:42   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 03/13] bpf: check CAP_SYS_ADMIN before looking up bpffs delegation names Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 17:03   ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 04/13] bpf: move bpf_get_btf_vmlinux() to btf.c Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 05/13] bpf: let btf_parse_module() adopt the caller's copy of the module BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 06/13] bpf: look up the struct_ops common value type in the given BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 07/13] bpf: parse the vmlinux BTF before taking cand_cache_mutex Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 08/13] tracing: parse the vmlinux BTF when BTF-typed output is enabled Jay Wang
2026-10-31 13:37   ` sashiko-bot
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 09/13] bpf: keep the vmlinux BTF zstd-compressed in the kernel image Jay Wang
2026-10-31 13:33   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 10/13] bpf: defer kfunc and struct_ops registrations until first BTF use Jay Wang
2026-10-31 13:38   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 17:07   ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 11/13] bpf: keep module BTF until the vmlinux BTF is parsed Jay Wang
2026-10-31 13:37   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 17:10     ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 12/13] selftests/bpf: keep ksyms and linked_vars working without __start_BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 13/13] Documentation/bpf: describe CONFIG_DEBUG_INFO_BTF_LAZY Jay Wang

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=20261031130959.23472-2-wanjay@amazon.com \
    --to=wanjay@amazon.com \
    --cc=abuehaze@amazon.com \
    --cc=almagui@kernel.org \
    --cc=andrii@kernel.org \
    --cc=arnd@arndb.de \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=doebel@amazon.de \
    --cc=eddyz87@gmail.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=jay.wang.upstream@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=memxor@gmail.com \
    --cc=mhiramat@kernel.org \
    --cc=mpohlack@amazon.de \
    --cc=nathan@kernel.org \
    --cc=nsc@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=x86@kernel.org \
    --cc=yonghong.song@linux.dev \
    /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.