Thread (27 messages) 27 messages, 5 authors, 11d ago

Re: [PATCH bpf-next v3 4/9] bpf: take the vmlinux BTF from the btf_vmlinux module

flat view

From: bot+bpf-ci@kernel.org
Date: 2026-09-25 23:23:29
Also in: bpf, linux-kbuild, lkml, rust-for-linux

quoted hunk ↗ jump to hunk
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 4b63bb91550a..81e6c65fe5f6 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -602,6 +602,7 @@ __u32 *btf_field_iter_next(struct btf_field_iter *it);
 const char *btf_name_by_offset(const struct btf *btf, u32 offset);
 const char *btf_str_by_offset(const struct btf *btf, u32 offset);
 struct btf *btf_parse_vmlinux(void);
+void *btf_vmlinux_data(u32 *size, bool load);
 struct bpf_prog_get_target_btf(const struct bpf_prog *prog);
 u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
 int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag);
[ ... ]
quoted hunk ↗ jump to hunk
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index da86162ba6d9..84968244a10f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -21922,26 +21922,51 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
 	return 0;
 }

+/*
+ * Returns the parsed vmlinux BTF, NULL if the kernel has none, or an ERR_PTR
+ * if it is malformed.  With CONFIG_DEBUG_INFO_BTF=m the BTF lives in the
+ * btf_vmlinux module; the first caller loads it and parses it.  May sleep.
+ */
 struct btf *bpf_get_btf_vmlinux(void)
 {
 	/* Pairs with the smp_store_release() on the parse path below. */
 	struct btf *btf = smp_load_acquire(&btf_vmlinux);
+	u32 size;

-	if (!btf && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
-		mutex_lock(&btf_vmlinux_lock);
-		btf = btf_vmlinux;
-		if (!btf) {
-			btf = btf_parse_vmlinux();
-			/*
-			 * Order the parsed BTF contents and the globals the
-			 * parse populated (e.g. bpf_ctx_convert.t) before
-			 * the pointer publication. Pairs with the acquire
-			 * on the lockless fast path above.
-			 */
-			smp_store_release(&btf_vmlinux, btf);
+	if (btf || !IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
+		return btf;
+
+	/*
+	 * Loading the module may take a while and its notifier must not be
+	 * blocked by us, so do it outside btf_vmlinux_lock.  Not available:
+	 * behave like a kernel without BTF, and retry next time.
+	 */
+	if (!btf_vmlinux_data(&size, true))
+		return NULL;
+
+	mutex_lock(&btf_vmlinux_lock);
+	btf = btf_vmlinux;
+	if (!btf) {
+		btf = btf_parse_vmlinux();
+		/*
+		 * With =m the BTF was checked against the kernel when the
+		 * module loaded, so a failure here is a resource problem
+		 * (-ENOMEM) rather than a broken BTF: do not remember it,
+		 * the next caller retries.
+		 */
+		if (IS_MODULE(CONFIG_DEBUG_INFO_BTF) && IS_ERR(btf)) {
+			mutex_unlock(&btf_vmlinux_lock);
+			return btf;
Does this error path account for deterministic parse failures?

The comment states that with CONFIG_DEBUG_INFO_BTF=m a parse failure
here is a resource problem (-ENOMEM) because the payload was checked
when the module loaded. btf_vmlinux_module_coming() does verify the
size and SHA-256 match the .BTF.meta values this kernel was built with.

However, btf_parse_vmlinux() calls btf_parse_base(), which can still
fail with errors that depend only on the content:

kernel/bpf/btf.c:btf_parse_base()
    btf_parse_hdr()           -> -E2BIG, -ENOENT, -EINVAL
    btf_parse_str_sec()       -> -EINVAL
    btf_check_all_metas()     -> -EINVAL for unknown kind or bad name
    btf_check_modifier_chain_length() -> -ELOOP

These checks are deterministic. A broken BTF would fail on every
attempt, yet the code never caches the error. With CONFIG_DEBUG_INFO_BTF=y
the same condition gives 'in-kernel BTF is malformed' and the ERR_PTR
is cached once. With =m, every later caller repeats the full parse of
the ~5.4 MiB BTF while holding btf_vmlinux_lock: kvmalloc of the types
array, a walk over all metas, then the free.

Callers include bpf_prog_load(), struct_ops map creation,
BPF_BTF_GET_NEXT_ID (which runs before the CAP_SYS_ADMIN check in
bpf_obj_get_next_id()), and read()/mmap() of the 0444
/sys/kernel/btf/vmlinux. An unprivileged user can therefore make the
kernel re-parse repeatedly and serialize other BTF users behind the
mutex.

Also, /sys/kernel/btf/vmlinux returns -ENODEV even though the raw bytes
are present, because btf_sysfs_vmlinux_load() requires a successful
parse.

Would it be better to cache only on content errors (e.g. all errors
except -ENOMEM) and retry resource failures, or to skip caching only
on specific errors?
quoted hunk ↗ jump to hunk
 		}
-		mutex_unlock(&btf_vmlinux_lock);
+		/*
+		 * Order the parsed BTF contents and the globals the
+		 * parse populated (e.g. bpf_ctx_convert.t) before
+		 * the pointer publication. Pairs with the acquire
+		 * on the lockless fast path above.
+		 */
+		smp_store_release(&btf_vmlinux, btf);
 	}
+	mutex_unlock(&btf_vmlinux_lock);
 	return btf;
 }
[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/36198628965
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help