From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Tejun Heo <tj@kernel.org>,
kkd@meta.com, kernel-team@meta.com
Subject: [PATCH bpf-next v1 06/10] libbpf: Bind the arena of an object to every program in it
Date: Mon, 12 Oct 2026 15:37:03 +0200 [thread overview]
Message-ID: <20261012133711.4030594-7-memxor@gmail.com> (raw)
In-Reply-To: <20261012133711.4030594-1-memxor@gmail.com>
A program has an arena only when one of its insns refers to the arena
map. The kernel can hand a program arena pointers all the same: as
__arena struct_ops arguments, through __arena struct members and as
kfunc return values. The verifier rejects those in a program without an
arena, since it has nowhere to place them, and so a struct_ops callback
that touches nothing but its arguments fails to load until it refers to
the arena artificially, e.g. with asm volatile("" :: "r"(&arena)).
Bind the arena of an object to every program loaded from it. The kernel
binds the maps of a continuous fd_array (fd_array_cnt) like maps an insn
refers to, so pass the arena in slot 0, which is free: a kfunc insn with
offset 0 names vmlinux BTF rather than a slot. The module BTF slots that
follow stay as they are. A feature probe keeps older kernels, which
reject a non-zero fd_array_cnt, on the previous behavior.
An object has at most one arena, libbpf refuses a second one, so there
is no ambiguity about which arena a program gets. Programs that never
touch the arena pay for the binding with the arena base setup in their
JIT prologue and with the arena showing up among their maps, and the
arena now lives as long as any program of the object. The light
skeleton loader is left alone.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
tools/lib/bpf/features.c | 40 +++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.c | 37 ++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf_internal.h | 2 ++
3 files changed, 79 insertions(+)
diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
index 9f589980beef..71c6a666afcb 100644
--- a/tools/lib/bpf/features.c
+++ b/tools/lib/bpf/features.c
@@ -77,6 +77,43 @@ static int probe_kern_global_data(int token_fd)
return probe_fd(ret);
}
+/*
+ * A continuous fd_array binds every map in it to the program, whether an
+ * insn refers to the map or not. Kernels without the attribute reject a
+ * non-zero fd_array_cnt.
+ */
+static int probe_kern_fd_array_cnt(int token_fd)
+{
+ struct bpf_insn insns[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ LIBBPF_OPTS(bpf_map_create_opts, map_opts,
+ .token_fd = token_fd,
+ .map_flags = token_fd ? BPF_F_TOKEN_FD : 0,
+ );
+ LIBBPF_OPTS(bpf_prog_load_opts, prog_opts,
+ .token_fd = token_fd,
+ .prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
+ .fd_array_cnt = 1,
+ );
+ int ret, map, insn_cnt = ARRAY_SIZE(insns);
+
+ map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_det_bind", sizeof(int), sizeof(int), 1,
+ &map_opts);
+ if (map < 0) {
+ ret = -errno;
+ pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
+ __func__, errstr(ret));
+ return ret;
+ }
+
+ prog_opts.fd_array = ↦
+ ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, &prog_opts);
+ close(map);
+ return probe_fd(ret);
+}
+
static int probe_kern_btf(int token_fd)
{
static const char strs[] = "\0int";
@@ -742,6 +779,9 @@ static struct kern_feature_desc {
[FEAT_PERCPU_DATA] = {
"kernel supports percpu data", probe_kern_percpu_data,
},
+ [FEAT_FD_ARRAY_CNT] = {
+ "continuous fd_array (fd_array_cnt)", probe_kern_fd_array_cnt,
+ },
};
bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index cb09ded90773..2360c8cc94d3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8799,6 +8799,38 @@ static int libbpf_prepare_prog_load(struct bpf_program *prog,
static void fixup_verifier_log(struct bpf_program *prog, char *buf, size_t buf_sz);
+/*
+ * Bind the arena of the object to the program about to be loaded. A program
+ * has an arena only when an insn refers to the map, but the kernel can hand
+ * it arena pointers all the same, as struct_ops arguments, through tagged
+ * struct members or as kfunc return values, and the verifier rejects those
+ * when the program has no arena to place them in. A continuous fd_array
+ * binds every map in it, the arena takes slot 0, which is free: a kfunc insn
+ * with offset 0 names vmlinux BTF rather than a slot.
+ */
+static int bpf_object__bind_arena(struct bpf_object *obj, struct bpf_prog_load_opts *load_attr)
+{
+ struct bpf_map *map;
+ int err;
+
+ if (obj->arena_map_idx < 0 || !kernel_supports(obj, FEAT_FD_ARRAY_CNT))
+ return 0;
+ map = &obj->maps[obj->arena_map_idx];
+ if (map->fd < 0)
+ return 0;
+
+ if (!obj->fd_array_cnt) {
+ err = libbpf_ensure_mem((void **)&obj->fd_array, &obj->fd_array_cap, sizeof(int), 1);
+ if (err)
+ return err;
+ obj->fd_array_cnt = 1;
+ }
+ obj->fd_array[0] = map->fd;
+ load_attr->fd_array = obj->fd_array;
+ load_attr->fd_array_cnt = obj->fd_array_cnt;
+ return 0;
+}
+
static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog,
struct bpf_insn *insns, int insns_cnt,
const char *license, __u32 kern_version, int *prog_fd)
@@ -8862,6 +8894,11 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
if (obj->data_in_arena)
load_attr.prog_flags |= BPF_F_ARENA_SCALAR;
load_attr.fd_array = obj->fd_array;
+ if (!obj->gen_loader) {
+ err = bpf_object__bind_arena(obj, &load_attr);
+ if (err)
+ return err;
+ }
load_attr.token_fd = obj->token_fd;
if (obj->token_fd)
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 4e45dfacb548..22d9fe512ea1 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -404,6 +404,8 @@ enum kern_feature_id {
FEAT_BPF_SYSCALL_COMMON_ATTRS,
/* Kernel supports percpu data */
FEAT_PERCPU_DATA,
+ /* Kernel binds every map in a continuous fd_array (fd_array_cnt) to the program */
+ FEAT_FD_ARRAY_CNT,
__FEAT_CNT,
};
--
2.53.0-Meta
next prev parent reply other threads:[~2026-10-12 13:37 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 13:36 [PATCH bpf-next v1 00/10] Fix some arena paper cuts Kumar Kartikeya Dwivedi
2026-10-12 13:36 ` [PATCH bpf-next v1 01/10] compiler_types: Add __arena and __nullable BTF type tag qualifiers Kumar Kartikeya Dwivedi
2026-10-12 13:36 ` [PATCH bpf-next v1 02/10] bpf: Recognize __nullable on pointer members of kernel structs Kumar Kartikeya Dwivedi
2026-10-12 13:54 ` sashiko-bot
2026-10-12 13:37 ` [PATCH bpf-next v1 03/10] bpf: Turn loads of __arena pointer members into arena pointers Kumar Kartikeya Dwivedi
2026-10-12 14:00 ` sashiko-bot
2026-10-12 14:27 ` bot+bpf-ci
2026-10-12 13:37 ` [PATCH bpf-next v1 04/10] selftests/bpf: Test __nullable pointer members Kumar Kartikeya Dwivedi
2026-10-12 14:27 ` bot+bpf-ci
2026-10-12 13:37 ` [PATCH bpf-next v1 05/10] selftests/bpf: Test __arena " Kumar Kartikeya Dwivedi
2026-10-12 14:27 ` bot+bpf-ci
2026-10-12 13:37 ` Kumar Kartikeya Dwivedi [this message]
2026-10-12 14:27 ` [PATCH bpf-next v1 06/10] libbpf: Bind the arena of an object to every program in it bot+bpf-ci
2026-10-12 13:37 ` [PATCH bpf-next v1 07/10] selftests/bpf: Test arena binding by libbpf Kumar Kartikeya Dwivedi
2026-10-12 13:44 ` sashiko-bot
2026-10-12 14:27 ` bot+bpf-ci
2026-10-12 13:37 ` [PATCH bpf-next v1 08/10] bpf: Set up the arena base only in functions that use it Kumar Kartikeya Dwivedi
2026-10-12 14:27 ` bot+bpf-ci
2026-10-12 13:37 ` [PATCH bpf-next v1 09/10] libbpf: Render the arena type tag with its address space Kumar Kartikeya Dwivedi
2026-10-12 13:37 ` [PATCH bpf-next v1 10/10] selftests/bpf: Test the address space rendering of the arena type tag Kumar Kartikeya Dwivedi
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=20261012133711.4030594-7-memxor@gmail.com \
--to=memxor@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=kernel-team@meta.com \
--cc=kkd@meta.com \
--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.