Skip to content

Commit c1e158d

Browse files
etsalKernel Patches Daemon
authored andcommitted
libbpf: Add gating for arena globals relocation feature
Add feature gating for the arena globals relocation introduced in commit c1f6117. The commit depends on a previous commit in the same patchset that is absent from older kernels (12a1fe6, "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 includes patch 12a1fe6, and only relocate arena globals if it does. CHANGELOG --------- v1 -> v2 (https://lore.kernel.org/bpf/20260209210240.2051209-1-emil@etsalapatis.com/) - Reworked test to use simpler array-based program (Andrii) Fixes: c1f6117 ("libbpf: Move arena globals to the end of the arena") Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
1 parent 4173ae7 commit c1e158d

File tree

3 files changed

+99
-2
lines changed

3 files changed

+99
-2
lines changed

tools/lib/bpf/features.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,95 @@ static int probe_kern_arg_ctx_tag(int token_fd)
506506
return probe_fd(prog_fd);
507507
}
508508

509+
static int probe_kern_arena_global_reloc(int token_fd)
510+
{
511+
const size_t bufsz = 1024;
512+
int btf_fd, prog_fd, map;
513+
char log_buf[bufsz];
514+
int ret;
515+
static const char strs[] = "\0f\0";
516+
const __u32 types[] = {
517+
/* [1] FUNC_PROTO `void(void)` */
518+
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 0), 0),
519+
/* [2] FUNC 'f' -> FUNC_PROTO (main prog) */
520+
BTF_TYPE_ENC(1 /* "f" */, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 1),
521+
};
522+
LIBBPF_OPTS(bpf_map_create_opts, map_opts,
523+
.token_fd = token_fd,
524+
.map_flags = token_fd ? BPF_F_TOKEN_FD : 0,
525+
);
526+
LIBBPF_OPTS(bpf_prog_load_opts, prog_opts,
527+
.token_fd = token_fd,
528+
.prog_flags = BPF_F_SLEEPABLE | (token_fd ? BPF_F_TOKEN_FD : 0),
529+
.log_buf = log_buf,
530+
.log_size = bufsz,
531+
);
532+
struct bpf_insn insns[] = {
533+
BPF_LD_MAP_VALUE(BPF_REG_1, 0, 1UL << 30),
534+
BPF_EXIT_INSN(),
535+
};
536+
const struct bpf_func_info_min func_infos[] = {
537+
{ 0, 2 }, /* main prog -> FUNC 'f' */
538+
};
539+
int insn_cnt = ARRAY_SIZE(insns);
540+
541+
btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types), strs, sizeof(strs), token_fd);
542+
if (btf_fd < 0)
543+
return 0; /* BTF not supported at all */
544+
545+
map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr", sizeof(int), 1, 1, &map_opts);
546+
if (map < 0) {
547+
ret = -errno;
548+
pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
549+
__func__, errstr(ret));
550+
close(btf_fd);
551+
return ret;
552+
}
553+
insns[0].imm = map;
554+
555+
prog_opts.prog_btf_fd = btf_fd;
556+
prog_opts.func_info = &func_infos;
557+
prog_opts.func_info_cnt = ARRAY_SIZE(func_infos);
558+
prog_opts.func_info_rec_size = sizeof(func_infos[0]);
559+
560+
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SYSCALL, "global_reloc", "GPL", insns, insn_cnt, &prog_opts);
561+
562+
ret = -errno;
563+
close(map);
564+
close(btf_fd);
565+
566+
if (prog_fd >= 0) {
567+
pr_warn("Error in %s(): Program loading unexpectedly succeeded.\n",
568+
__func__);
569+
close(prog_fd);
570+
return -EINVAL;
571+
}
572+
573+
/* Be conservative and NULL terminate the buffer to ensure strstr terminates. */
574+
log_buf[bufsz - 1] = '\0';
575+
576+
/*
577+
* Feature is allowed if we're not failing with the error message "direct value
578+
* offset of %u is not allowed that was removed in 12a1fe6e12db
579+
* ("bpf/verifier: Do not limit maximum direct offset into arena map").
580+
* Instead, we should be failing with the message "invalid access to map value
581+
* pointer". Ensure we match with one of the two and we're not failing with a
582+
* different, unexpected message.
583+
*/
584+
if (strstr(log_buf, "direct value offset of"))
585+
return 0;
586+
587+
if (strstr(log_buf, "invalid access to map value pointer") == NULL) {
588+
pr_warn("Error in %s(): Program unexpectedly failed with message: %s.\n",
589+
__func__, log_buf);
590+
return ret;
591+
}
592+
593+
/* Feature is on. */
594+
595+
return 1;
596+
}
597+
509598
typedef int (*feature_probe_fn)(int /* token_fd */);
510599

511600
static struct kern_feature_cache feature_cache;
@@ -581,6 +670,9 @@ static struct kern_feature_desc {
581670
[FEAT_BTF_QMARK_DATASEC] = {
582671
"BTF DATASEC names starting from '?'", probe_kern_btf_qmark_datasec,
583672
},
673+
[FEAT_ARENA_GLOBAL_RELOC] = {
674+
"Relocatable arena globals support", probe_kern_arena_global_reloc,
675+
},
584676
};
585677

586678
bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)

tools/lib/bpf/libbpf.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3009,8 +3009,11 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
30093009
memcpy(obj->arena_data, data, data_sz);
30103010
obj->arena_data_sz = data_sz;
30113011

3012-
/* place globals at the end of the arena */
3013-
obj->arena_data_off = mmap_sz - data_alloc_sz;
3012+
/* place globals at the end of the arena (if supported) */
3013+
if (kernel_supports(obj, FEAT_ARENA_GLOBAL_RELOC))
3014+
obj->arena_data_off = mmap_sz - data_alloc_sz;
3015+
else
3016+
obj->arena_data_off = 0;
30143017

30153018
/* make bpf_map__init_value() work for ARENA maps */
30163019
map->mmaped = obj->arena_data;

tools/lib/bpf/libbpf_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ enum kern_feature_id {
392392
FEAT_ARG_CTX_TAG,
393393
/* Kernel supports '?' at the front of datasec names */
394394
FEAT_BTF_QMARK_DATASEC,
395+
/* Kernel supports relocating arena globals to end of the arena */
396+
FEAT_ARENA_GLOBAL_RELOC,
395397
__FEAT_CNT,
396398
};
397399

0 commit comments

Comments
 (0)