]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
libbpf: Add gating for arena globals relocation feature
authorEmil Tsalapatis <emil@etsalapatis.com>
Tue, 10 Feb 2026 18:45:32 +0000 (13:45 -0500)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 13 Feb 2026 22:14:27 +0000 (14:14 -0800)
Add feature gating for the arena globals relocation introduced in
commit c1f61171d44b. The commit depends on a previous commit in the
same patchset that is absent from older kernels
(12a1fe6e12db "bpf/verifier: Do not limit maximum direct offset into arena map").

Without this commit, arena globals relocation with arenas >= 512MiB
fails to load and breaks libbpf's backwards compatibility.

Introduce a libbpf feature to check whether the running kernel allows for
full range ldimm64 offset, and only relocate arena globals if it does.

Fixes: c1f61171d44b ("libbpf: Move arena globals to the end of the arena")
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260210184532.255475-1-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/lib/bpf/features.c
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf_internal.h

index b842b83e24804e4b18005c7ef60cd3f1ec5365a4..b65ab109e3ff41f5e9a37e8103bffe0e862f2d71 100644 (file)
@@ -506,6 +506,67 @@ static int probe_kern_arg_ctx_tag(int token_fd)
        return probe_fd(prog_fd);
 }
 
+static int probe_ldimm64_full_range_off(int token_fd)
+{
+       char log_buf[1024];
+       int prog_fd, map_fd;
+       int ret;
+       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,
+               .log_buf = log_buf,
+               .log_size = sizeof(log_buf),
+       );
+       struct bpf_insn insns[] = {
+               BPF_LD_MAP_VALUE(BPF_REG_1, 0, 1UL << 30),
+               BPF_EXIT_INSN(),
+       };
+       int insn_cnt = ARRAY_SIZE(insns);
+
+       map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr", sizeof(int), 1, 1, &map_opts);
+       if (map_fd < 0) {
+               ret = -errno;
+               pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
+                       __func__, errstr(ret));
+               return ret;
+       }
+       insns[0].imm = map_fd;
+
+       prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, "global_reloc", "GPL", insns, insn_cnt, &prog_opts);
+       ret = -errno;
+
+       close(map_fd);
+       close(prog_fd);
+
+       if (prog_fd >= 0) {
+               pr_warn("Error in %s(): Program loading unexpectedly succeeded.\n", __func__);
+               return -EINVAL;
+       }
+
+       /*
+        * Feature is allowed if we're not failing with the error message
+        * "direct value offset of %u is not allowed" removed in
+        * 12a1fe6e12db ("bpf/verifier: Do not limit maximum direct offset into arena map").
+        * We should instead fail with "invalid access to map value pointer".
+        * Ensure we match with one of the two and we're not failing with a
+        * different, unexpected message.
+        */
+       if (strstr(log_buf, "direct value offset of"))
+               return 0;
+
+       if (!strstr(log_buf, "invalid access to map value pointer")) {
+               pr_warn("Error in %s(): Program unexpectedly failed with message: %s.\n",
+                       __func__, log_buf);
+               return ret;
+       }
+
+       return 1;
+}
+
 typedef int (*feature_probe_fn)(int /* token_fd */);
 
 static struct kern_feature_cache feature_cache;
@@ -581,6 +642,9 @@ static struct kern_feature_desc {
        [FEAT_BTF_QMARK_DATASEC] = {
                "BTF DATASEC names starting from '?'", probe_kern_btf_qmark_datasec,
        },
+       [FEAT_LDIMM64_FULL_RANGE_OFF] = {
+               "full range LDIMM64 support", probe_ldimm64_full_range_off,
+       },
 };
 
 bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)
index 0c8bf0b5cce44462414e99a958260e940148e818..93e59ed8d9a18db2151b10da329eca9ef8f60eee 100644 (file)
@@ -3009,8 +3009,11 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
        memcpy(obj->arena_data, data, data_sz);
        obj->arena_data_sz = data_sz;
 
-       /* place globals at the end of the arena */
-       obj->arena_data_off = mmap_sz - data_alloc_sz;
+       /* place globals at the end of the arena (if supported) */
+       if (kernel_supports(obj, FEAT_LDIMM64_FULL_RANGE_OFF))
+               obj->arena_data_off = mmap_sz - data_alloc_sz;
+       else
+               obj->arena_data_off = 0;
 
        /* make bpf_map__init_value() work for ARENA maps */
        map->mmaped = obj->arena_data;
index fc59b21b51b5e5353f25349b0f225ec32c944255..974147e8a8aae97d5d723e5ffc39fbd2141fa94f 100644 (file)
@@ -392,6 +392,8 @@ enum kern_feature_id {
        FEAT_ARG_CTX_TAG,
        /* Kernel supports '?' at the front of datasec names */
        FEAT_BTF_QMARK_DATASEC,
+       /* Kernel supports LDIMM64 imm offsets past 512 MiB. */
+       FEAT_LDIMM64_FULL_RANGE_OFF,
        __FEAT_CNT,
 };