* [PATCH bpf-next v3 0/6] Minimize annotations for arena programs
@ 2026-06-02 0:41 Emil Tsalapatis
2026-06-02 0:41 ` [PATCH bpf-next v3 1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier Emil Tsalapatis
` (6 more replies)
0 siblings, 7 replies; 14+ messages in thread
From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw)
To: bpf
Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski,
Emil Tsalapatis
BPF programs must currently include code to address two limitations
of function signatures that include arena types. First, arena arguments
must be annotated with __arg_arena in the function signature in addition
to __arena. Second, it is currently not allowed to return an arena pointer
from a subprog, even though it is safe to do so. These limitations require
extra annotations and typecasts respectively, and have proven sources of
confusion to programmers.
The patchset improves arena-related function signatures in two ways.
First, it removes the need for __arg_arena in function signatures.
Second, it allows subprogs to directly return arena pointers to their
caller.
To do this we add a new type tag to the existing __arena annotation.
The annotation is currently an alias for __attribute__((address_space(1))),
which is not discoverable from BTF alone and so cannot be used to
determine whether a pointer variable is an arena pointer during
verification. With the new type tag, we can determine whether
either the arguments and or the return value of a function belong
in an arena.
We test the new code by modifying libarena to take advantage of these
relaxed limitations.
CHANGELOG
=========
v2 -> v3 (https://lore.kernel.org/bpf/20260530002259.4505-1-emil@etsalapatis.com/)
- Added Acks by Eduard
- Complete the __arg_arena removal by removing them from htab (Alexei)
- Add a test in verifier_arena_globals1.c to confirm the new __arena attribute
works as expected in function argument and return types
- Reject type tags on non-pointer types, currently only possible in handcrafted
BTF (Eduard)
- Undo inaccurate change on verifier comment (AI)
- Fix error return value for invalid BTF return types during BTF parsing (Eduard)
v1 -> v2 (lore.kernel.org/bpf/20260527071457.4598-1-emil@etsalapatis.com/)
- Rebased to fix conflict
- Removed the typedef foo * foo_t typedefs. Those were necessary to avoid
annotating each instance of the type with __arena. The new version of the
patch instead removes typedefs and uses __arena everywhere directly (see
patch 4/5 for more details).
- Reorganized the patchset to frontload all kernel-side changes and place
the libarena changes at the end.
Emil Tsalapatis (6):
selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier
verifier: parse BTF type tags for function arguments
bpf: Allow subprogs to return arena pointers
selftests/bpf: Remove __arg_arena from the codebase
selftests/bpf: libarena: Directly return arena pointers from functions
selftests/bpf: Add tests for the new type-tag based __arena identifier
kernel/bpf/btf.c | 178 +++++++++++++-----
kernel/bpf/verifier.c | 4 +
tools/testing/selftests/bpf/bpf_arena_htab.h | 11 +-
.../selftests/bpf/bpf_arena_strsearch.h | 4 +-
.../bpf/libarena/include/bpf_arena_common.h | 5 +-
.../libarena/include/bpf_arena_spin_lock.h | 6 +-
.../bpf/libarena/include/libarena/asan.h | 6 +-
.../bpf/libarena/include/libarena/buddy.h | 23 +--
.../bpf/libarena/include/libarena/common.h | 3 +-
.../libarena/selftests/st_asan_buddy.bpf.c | 4 +-
.../bpf/libarena/selftests/st_asan_common.h | 2 +-
.../bpf/libarena/selftests/st_buddy.bpf.c | 2 +-
.../selftests/bpf/libarena/src/asan.bpf.c | 38 ++--
.../selftests/bpf/libarena/src/buddy.bpf.c | 96 +++++-----
.../selftests/bpf/libarena/src/common.bpf.c | 10 +-
.../selftests/bpf/progs/arena_spin_lock.c | 1 +
.../selftests/bpf/progs/verifier_arena.c | 67 +++++++
17 files changed, 298 insertions(+), 162 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH bpf-next v3 1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments Emil Tsalapatis ` (5 subsequent siblings) 6 siblings, 0 replies; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis The arena qualifier currently designates its associated type as belonging to address space 1. This property affects code generation, but is not reflected in the BTF information of the function. This lack of information at the BTF level prevents us from returning arena pointers from global subprograms. Subprogs cannot return any data structure more complex than a scalar, so pointers to structs are rejected as a return type. We have no way of marking the return type as a pointer to an arena, which is safe provided the two subprogs have the same arena. Expand the __arena qualifier to also attach a BTF type tag to the type. This lets us determine whether a variable belongs to an arena from its type alone through BTF parsing. Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h index 16f8ce832004..445be3c4edec 100644 --- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h +++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h @@ -33,7 +33,7 @@ #endif #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && !defined(BPF_ARENA_FORCE_ASM) -#define __arena __attribute__((address_space(1))) +#define __arena __attribute__((address_space(1))) __attribute__((btf_type_tag("arena"))) #define __arena_global __attribute__((address_space(1))) #define cast_kern(ptr) /* nop for bpf prog. emitted by LLVM */ #define cast_user(ptr) /* nop for bpf prog. emitted by LLVM */ -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 1:05 ` sashiko-bot 2026-06-02 1:26 ` bot+bpf-ci 2026-06-02 0:41 ` [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers Emil Tsalapatis ` (4 subsequent siblings) 6 siblings, 2 replies; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis The BTF parsing logic for function arguments goes through the arguments' decl tags, but does not go into their type tags. Add type tag parsing for function arguments. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- kernel/bpf/btf.c | 120 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 35 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 17d4ab0a8206..c6a930aca67e 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7802,6 +7802,84 @@ enum btf_arg_tag { ARG_TAG_ARENA = BIT_ULL(5), }; +static int btf_scan_decl_tags(struct bpf_verifier_env *env, + const struct btf *btf, + const struct btf_type *fn_t, + u32 arg_idx, bool is_global, u32 *tags) +{ + int id = btf_named_start_id(btf, false) - 1; + + /* + * The 'arg:<tag>' decl_tag takes precedence over the derivation + * of the register type from the BTF type itself. + */ + while ((id = btf_find_next_decl_tag(btf, fn_t, arg_idx, "arg:", id)) > 0) { + const struct btf_type *tag_t = btf_type_by_id(btf, id); + const char *tag = __btf_name_by_offset(btf, tag_t->name_off) + 4; + + /* disallow arg tags in static subprogs */ + if (!is_global) { + bpf_log(&env->log, + "arg#%d type tag is not supported in static functions\n", + arg_idx); + return -EOPNOTSUPP; + } + + if (strcmp(tag, "ctx") == 0) { + *tags |= ARG_TAG_CTX; + } else if (strcmp(tag, "trusted") == 0) { + *tags |= ARG_TAG_TRUSTED; + } else if (strcmp(tag, "untrusted") == 0) { + *tags |= ARG_TAG_UNTRUSTED; + } else if (strcmp(tag, "nonnull") == 0) { + *tags |= ARG_TAG_NONNULL; + } else if (strcmp(tag, "nullable") == 0) { + *tags |= ARG_TAG_NULLABLE; + } else if (strcmp(tag, "arena") == 0) { + *tags |= ARG_TAG_ARENA; + } else { + bpf_log(&env->log, "arg#%d has unsupported set of tags\n", arg_idx); + return -EOPNOTSUPP; + } + } + if (id != -ENOENT) { + bpf_log(&env->log, "arg#%d type tag fetching failure: %d\n", arg_idx, id); + return id; + } + + return 0; +} + +static int btf_scan_type_tags(struct bpf_verifier_env *env, + const struct btf *btf, u32 type_id, + u32 *tags) +{ + const struct btf_type *t; + + /* Find the first pointer type in the chain. */ + t = btf_type_skip_modifiers(btf, type_id, NULL); + if (!t || !btf_type_is_ptr(t)) + return 0; + + /* We got a pointer, get all associated type tags. */ + t = btf_type_by_id(btf, t->type); + while (t && btf_type_is_type_tag(t)) { + const char *tag = __btf_name_by_offset(btf, t->name_off); + + if (strcmp(tag, "arena") == 0) { + *tags |= ARG_TAG_ARENA; + } else { + bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", + tag); + return -EOPNOTSUPP; + } + + t = btf_type_by_id(btf, t->type); + } + + return 0; +} + /* Process BTF of a function to produce high-level expectation of function * arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information * is cached in subprog info for reuse. @@ -7820,6 +7898,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) struct btf *btf = prog->aux->btf; const struct btf_param *args; const struct btf_type *t, *ref_t, *fn_t; + int err; u32 i, nargs, btf_id; const char *tname; @@ -7903,42 +7982,13 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) */ for (i = 0; i < nargs; i++) { u32 tags = 0; - int id = btf_named_start_id(btf, false) - 1; - - /* 'arg:<tag>' decl_tag takes precedence over derivation of - * register type from BTF type itself - */ - while ((id = btf_find_next_decl_tag(btf, fn_t, i, "arg:", id)) > 0) { - const struct btf_type *tag_t = btf_type_by_id(btf, id); - const char *tag = __btf_name_by_offset(btf, tag_t->name_off) + 4; - - /* disallow arg tags in static subprogs */ - if (!is_global) { - bpf_log(log, "arg#%d type tag is not supported in static functions\n", i); - return -EOPNOTSUPP; - } + err = btf_scan_decl_tags(env, btf, fn_t, i, is_global, &tags); + if (err) + return err; - if (strcmp(tag, "ctx") == 0) { - tags |= ARG_TAG_CTX; - } else if (strcmp(tag, "trusted") == 0) { - tags |= ARG_TAG_TRUSTED; - } else if (strcmp(tag, "untrusted") == 0) { - tags |= ARG_TAG_UNTRUSTED; - } else if (strcmp(tag, "nonnull") == 0) { - tags |= ARG_TAG_NONNULL; - } else if (strcmp(tag, "nullable") == 0) { - tags |= ARG_TAG_NULLABLE; - } else if (strcmp(tag, "arena") == 0) { - tags |= ARG_TAG_ARENA; - } else { - bpf_log(log, "arg#%d has unsupported set of tags\n", i); - return -EOPNOTSUPP; - } - } - if (id != -ENOENT) { - bpf_log(log, "arg#%d type tag fetching failure: %d\n", i, id); - return id; - } + err = btf_scan_type_tags(env, btf, args[i].type, &tags); + if (err) + return err; t = btf_type_by_id(btf, args[i].type); while (btf_type_is_modifier(t)) -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments 2026-06-02 0:41 ` [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments Emil Tsalapatis @ 2026-06-02 1:05 ` sashiko-bot 2026-06-02 1:05 ` Emil Tsalapatis 2026-06-02 1:26 ` bot+bpf-ci 1 sibling, 1 reply; 14+ messages in thread From: sashiko-bot @ 2026-06-02 1:05 UTC (permalink / raw) To: Emil Tsalapatis; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Critical] The `btf_scan_type_tags` function unconditionally rejects any `type_tag` other than "arena", breaking previously valid BPF programs. -- commit 1b7f5032b014c339265f4b7f34961a16eb4c5894 Author: Emil Tsalapatis <emil@etsalapatis.com> verifier: parse BTF type tags for function arguments The BTF parsing logic for function arguments goes through the arguments' decl tags, but does not go into their type tags. Add type tag parsing for function arguments. > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c > index 17d4ab0a82068..c6a930aca67ec 100644 > --- a/kernel/bpf/btf.c > +++ b/kernel/bpf/btf.c [ ... ] > +static int btf_scan_type_tags(struct bpf_verifier_env *env, > + const struct btf *btf, u32 type_id, > + u32 *tags) > +{ > + const struct btf_type *t; > + > + /* Find the first pointer type in the chain. */ > + t = btf_type_skip_modifiers(btf, type_id, NULL); > + if (!t || !btf_type_is_ptr(t)) > + return 0; > + > + /* We got a pointer, get all associated type tags. */ > + t = btf_type_by_id(btf, t->type); > + while (t && btf_type_is_type_tag(t)) { > + const char *tag = __btf_name_by_offset(btf, t->name_off); > + > + if (strcmp(tag, "arena") == 0) { > + *tags |= ARG_TAG_ARENA; > + } else { > + bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", > + tag); > + return -EOPNOTSUPP; > + } [Severity: Critical] Will this unconditionally reject any type tags other than "arena", causing previously valid BPF programs to fail verification? Since tags like "user", "rcu", or "percpu" are commonly used in kernel headers, and were previously skipped by btf_type_skip_modifiers(), returning -EOPNOTSUPP here will propagate through btf_prepare_func_args() and abort loading global subprograms that use these type tags. Could this break backward compatibility for existing userspace programs? > + > + t = btf_type_by_id(btf, t->type); > + } > + > + return 0; > +} -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602004120.17087-1-emil@etsalapatis.com?part=2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments 2026-06-02 1:05 ` sashiko-bot @ 2026-06-02 1:05 ` Emil Tsalapatis 0 siblings, 0 replies; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 1:05 UTC (permalink / raw) To: sashiko-reviews, Emil Tsalapatis; +Cc: bpf On Mon Jun 1, 2026 at 9:05 PM EDT, sashiko-bot wrote: > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > - [Critical] The `btf_scan_type_tags` function unconditionally rejects any `type_tag` other than "arena", breaking previously valid BPF programs. > -- > > commit 1b7f5032b014c339265f4b7f34961a16eb4c5894 > Author: Emil Tsalapatis <emil@etsalapatis.com> > > verifier: parse BTF type tags for function arguments > > The BTF parsing logic for function arguments goes through > the arguments' decl tags, but does not go into their type > tags. Add type tag parsing for function arguments. > >> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c >> index 17d4ab0a82068..c6a930aca67ec 100644 >> --- a/kernel/bpf/btf.c >> +++ b/kernel/bpf/btf.c > > [ ... ] > >> +static int btf_scan_type_tags(struct bpf_verifier_env *env, >> + const struct btf *btf, u32 type_id, >> + u32 *tags) >> +{ >> + const struct btf_type *t; >> + >> + /* Find the first pointer type in the chain. */ >> + t = btf_type_skip_modifiers(btf, type_id, NULL); >> + if (!t || !btf_type_is_ptr(t)) >> + return 0; >> + >> + /* We got a pointer, get all associated type tags. */ >> + t = btf_type_by_id(btf, t->type); >> + while (t && btf_type_is_type_tag(t)) { >> + const char *tag = __btf_name_by_offset(btf, t->name_off); >> + >> + if (strcmp(tag, "arena") == 0) { >> + *tags |= ARG_TAG_ARENA; >> + } else { >> + bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", >> + tag); >> + return -EOPNOTSUPP; >> + } > > [Severity: Critical] > Will this unconditionally reject any type tags other than "arena", causing > previously valid BPF programs to fail verification? > > Since tags like "user", "rcu", or "percpu" are commonly used in kernel > headers, and were previously skipped by btf_type_skip_modifiers(), returning > -EOPNOTSUPP here will propagate through btf_prepare_func_args() and abort > loading global subprograms that use these type tags. > > Could this break backward compatibility for existing userspace programs? Already addressed in previous round. > >> + >> + t = btf_type_by_id(btf, t->type); >> + } >> + >> + return 0; >> +} ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments 2026-06-02 0:41 ` [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments Emil Tsalapatis 2026-06-02 1:05 ` sashiko-bot @ 2026-06-02 1:26 ` bot+bpf-ci 1 sibling, 0 replies; 14+ messages in thread From: bot+bpf-ci @ 2026-06-02 1:26 UTC (permalink / raw) To: emil, bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, emil, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai [-- Attachment #1: Type: text/plain, Size: 2338 bytes --] > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c > index 17d4ab0a8206..c6a930aca67e 100644 > --- a/kernel/bpf/btf.c > +++ b/kernel/bpf/btf.c [ ... ] > +static int btf_scan_type_tags(struct bpf_verifier_env *env, > + const struct btf *btf, u32 type_id, > + u32 *tags) > +{ > + const struct btf_type *t; > + > + /* Find the first pointer type in the chain. */ > + t = btf_type_skip_modifiers(btf, type_id, NULL); > + if (!t || !btf_type_is_ptr(t)) > + return 0; Should this return -EOPNOTSUPP for non-pointer types with type tags? Eduard Zingerman suggested this in v2, and you agreed it would be appropriate for handcrafted BTF, but the current code still returns 0. Reference: https://lore.kernel.org/bpf/e1a40461fa792dc7687b0029f3ede546eeeacbc9.camel@gmail.com/ > + > + /* We got a pointer, get all associated type tags. */ > + t = btf_type_by_id(btf, t->type); > + while (t && btf_type_is_type_tag(t)) { Does this loop handle type tags that are separated from the pointer by other modifiers like CONST, VOLATILE, or TYPEDEF? If the BTF chain is structured as: PTR -> CONST -> TYPE_TAG("arena") -> INT the loop would evaluate btf_type_is_type_tag(t) at the CONST modifier, get false, and exit immediately. The arena type tag would never be collected and ARG_TAG_ARENA would not be set. This causes arena-qualified pointer arguments to be mis-classified, defeating the feature this patch series adds. The issue is confirmed by commit 7e64879a17df ("bpf: Allow subprogs to return arena pointers") which rewrites this loop to iterate over btf_type_is_modifier(t) with a continue statement to skip non-type-tag modifiers. Reference: https://lore.kernel.org/bpf/20260530005922.90CEE1F00893@smtp.kernel.org/ > + const char *tag = __btf_name_by_offset(btf, t->name_off); > + > + if (strcmp(tag, "arena") == 0) { > + *tags |= ARG_TAG_ARENA; > + } else { > + bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", > + tag); > + return -EOPNOTSUPP; > + } > + > + t = btf_type_by_id(btf, t->type); > + } > + > + return 0; > +} [ ... ] --- 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/26791686730 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 1:20 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase Emil Tsalapatis ` (3 subsequent siblings) 6 siblings, 1 reply; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis BPF subprogs currently only return void or scalar values. However, it is also safe to return arena pointers between subprogs in the same BPF program: Arena pointers are guaranteed to be safe for both programs at any point. Expand the verifier to permit returning an arena pointer to the caller. The main subprog is still not allowed to return an arena pointer because arena pointers are internal to the BPF program, and the return values permitted for each main subprog depend on the program type anyway. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- kernel/bpf/btf.c | 66 ++++++++++++++++++++++++++++++++----------- kernel/bpf/verifier.c | 4 +++ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index c6a930aca67e..3bd44a22dd06 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7858,12 +7858,22 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, /* Find the first pointer type in the chain. */ t = btf_type_skip_modifiers(btf, type_id, NULL); + + /* + * We currently reject type tags on non-pointer types, + * which neither LLVM nor GCC support anyway. + */ if (!t || !btf_type_is_ptr(t)) return 0; /* We got a pointer, get all associated type tags. */ - t = btf_type_by_id(btf, t->type); - while (t && btf_type_is_type_tag(t)) { + for (t = btf_type_by_id(btf, t->type); t && btf_type_is_modifier(t); + t = btf_type_by_id(btf, t->type)) { + + /* Skip non-type tag modifiers. */ + if (!btf_type_is_type_tag(t)) + continue; + const char *tag = __btf_name_by_offset(btf, t->name_off); if (strcmp(tag, "arena") == 0) { @@ -7873,13 +7883,39 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, tag); return -EOPNOTSUPP; } - - t = btf_type_by_id(btf, t->type); } return 0; } +/* Check whether the type is a valid return type. */ +static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *btf, + const struct btf_type *t, int subprog) +{ + u32 tags = 0; + int err; + + err = btf_scan_type_tags(env, btf, t->type, &tags); + if (err) + return err; + + t = btf_type_skip_modifiers(btf, t->type, NULL); + + /* + * We allow all subprogs except for the main one to return any kind of arena pointer. + * General arena variables are not allowed, since it makes no sense to return by value + * a variable that's on the heap in the first place. + */ + if (subprog && (tags & ARG_TAG_ARENA) && btf_type_is_ptr(t)) + return 0; + + /* We always accept void or scalars. */ + if (btf_type_is_void(t) || btf_type_is_int(t) || btf_is_any_enum(t)) + return 0; + + return -EOPNOTSUPP; +} + /* Process BTF of a function to produce high-level expectation of function * arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information * is cached in subprog info for reuse. @@ -7963,18 +7999,16 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) tname, nargs, MAX_BPF_FUNC_REG_ARGS); return -EINVAL; } - /* check that function is void or returns int, exception cb also requires this */ - t = btf_type_by_id(btf, t->type); - while (btf_type_is_modifier(t)) - t = btf_type_by_id(btf, t->type); - if (!btf_type_is_void(t) && !btf_type_is_int(t) && !btf_is_any_enum(t)) { - if (!is_global) - return -EINVAL; - bpf_log(log, - "Global function %s() return value not void or scalar. " - "Only those are supported.\n", - tname); - return -EINVAL; + + err = btf_validate_return_type(env, btf, t, subprog); + if (err) { + if (is_global) { + bpf_log(log, + "Global function %s() return value not void or scalar. " + "Only those are supported.\n", + tname); + } + return err; } /* Convert BTF function arguments into verifier types. diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c8d980fdd709..1ded44652c83 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -16645,6 +16645,10 @@ static int check_global_subprog_return_code(struct bpf_verifier_env *env) if (err) return err; + /* Pointers to arena are safe to pass between subprograms. */ + if (is_arena_reg(env, BPF_REG_0)) + return 0; + if (is_pointer_value(env, BPF_REG_0)) { verbose(env, "R%d leaks addr as return value\n", BPF_REG_0); return -EACCES; -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers 2026-06-02 0:41 ` [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers Emil Tsalapatis @ 2026-06-02 1:20 ` sashiko-bot 0 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-06-02 1:20 UTC (permalink / raw) To: Emil Tsalapatis; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The verifier incorrectly marks the return value of global subprograms returning arena pointers as `SCALAR_VALUE` in the caller's state, preventing native pointer use. -- commit 3756bfe1c313ae3bcfb27afcfb3650b07638ef91 Author: Emil Tsalapatis <emil@etsalapatis.com> bpf: Allow subprogs to return arena pointers This commit expands the verifier to permit returning an arena pointer to the caller from a subprogram. It updates btf_validate_return_type and check_global_subprog_return_code to allow subprograms to return PTR_TO_ARENA without failing their own verification. > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 3a270bc485c20..9dac790905b63 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -16651,6 +16651,10 @@ static int check_global_subprog_return_code(struct bpf_verifier_env *env) > if (err) > return err; > > + /* Pointers to arena are safe to pass between subprograms. */ > + if (is_arena_reg(env, BPF_REG_0)) > + return 0; > + [Severity: Medium] Will this change be sufficient for the caller to actually receive a PTR_TO_ARENA? Looking at the caller-side logic in check_func_call(), it appears BPF_REG_0 is still unconditionally marked as a SCALAR_VALUE for all non-void global subprograms: kernel/bpf/verifier.c:check_func_call() { ... /* All non-void global functions return a 64-bit SCALAR_VALUE. */ if (!subprog_returns_void(env, subprog)) { mark_reg_unknown(env, caller->regs, BPF_REG_0); caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; } ... } Could this cause the caller to receive a scalar instead of the intended arena pointer, leading the verifier to reject subsequent dereferences with an invalid mem access error? > if (is_pointer_value(env, BPF_REG_0)) { > verbose(env, "R%d leaks addr as return value\n", BPF_REG_0); > return -EACCES; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602004120.17087-1-emil@etsalapatis.com?part=3 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis ` (2 preceding siblings ...) 2026-06-02 0:41 ` [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 1:31 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions Emil Tsalapatis ` (2 subsequent siblings) 6 siblings, 1 reply; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis Now that BPF __arg_arena has been subsumed by __arena, remove __arg_arena from the codebase. This way the user has one fewer annotation to worry about. To remove __arg_arena we remove the typedefs we were previously using to minimize __arena annotations. This is because __arena now also includes a BTF type tag, which is ignored for non-pointer types. As a result, we cannot capture the whole __arena annotation inside a typedef and need to directly annotate the pointer type when declaring the variable. The extra verbosity is worth it because the use of the __arena tag is intuitive to the programmer and removes the __arg_arena tag that has been a consistent source of confusion for users. The typedefs can be reintroduced later (without __arg_arena) once compilers start supporting BTF type tags for non-pointer types. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- tools/testing/selftests/bpf/bpf_arena_htab.h | 11 ++- .../selftests/bpf/bpf_arena_strsearch.h | 4 +- .../bpf/libarena/include/bpf_arena_common.h | 3 +- .../libarena/include/bpf_arena_spin_lock.h | 6 +- .../bpf/libarena/include/libarena/asan.h | 6 +- .../bpf/libarena/include/libarena/buddy.h | 22 ++--- .../libarena/selftests/st_asan_buddy.bpf.c | 4 +- .../bpf/libarena/selftests/st_asan_common.h | 2 +- .../bpf/libarena/selftests/st_buddy.bpf.c | 2 +- .../selftests/bpf/libarena/src/asan.bpf.c | 38 ++++----- .../selftests/bpf/libarena/src/buddy.bpf.c | 80 +++++++++---------- .../selftests/bpf/libarena/src/common.bpf.c | 6 +- .../selftests/bpf/progs/arena_spin_lock.c | 1 + 13 files changed, 84 insertions(+), 101 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_arena_htab.h b/tools/testing/selftests/bpf/bpf_arena_htab.h index acc01a876668..d7ba86362d86 100644 --- a/tools/testing/selftests/bpf/bpf_arena_htab.h +++ b/tools/testing/selftests/bpf/bpf_arena_htab.h @@ -14,9 +14,8 @@ struct htab { htab_bucket_t *buckets; int n_buckets; }; -typedef struct htab __arena htab_t; -static inline htab_bucket_t *__select_bucket(htab_t *htab, __u32 hash) +static inline htab_bucket_t *__select_bucket(struct htab __arena *htab, __u32 hash) { htab_bucket_t *b = htab->buckets; @@ -24,7 +23,7 @@ static inline htab_bucket_t *__select_bucket(htab_t *htab, __u32 hash) return &b[hash & (htab->n_buckets - 1)]; } -static inline arena_list_head_t *select_bucket(htab_t *htab, __u32 hash) +static inline arena_list_head_t *select_bucket(struct htab __arena *htab, __u32 hash) { return &__select_bucket(htab, hash)->head; } @@ -53,7 +52,7 @@ static int htab_hash(int key) return key; } -__weak int htab_lookup_elem(htab_t *htab __arg_arena, int key) +__weak int htab_lookup_elem(struct htab __arena *htab, int key) { hashtab_elem_t *l_old; arena_list_head_t *head; @@ -66,7 +65,7 @@ __weak int htab_lookup_elem(htab_t *htab __arg_arena, int key) return 0; } -__weak int htab_update_elem(htab_t *htab __arg_arena, int key, int value) +__weak int htab_update_elem(struct htab __arena *htab, int key, int value) { hashtab_elem_t *l_new = NULL, *l_old; arena_list_head_t *head; @@ -90,7 +89,7 @@ __weak int htab_update_elem(htab_t *htab __arg_arena, int key, int value) return 0; } -void htab_init(htab_t *htab) +void htab_init(struct htab __arena *htab) { void __arena *buckets = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0); diff --git a/tools/testing/selftests/bpf/bpf_arena_strsearch.h b/tools/testing/selftests/bpf/bpf_arena_strsearch.h index f0d575daef5a..10a70667c8bf 100644 --- a/tools/testing/selftests/bpf/bpf_arena_strsearch.h +++ b/tools/testing/selftests/bpf/bpf_arena_strsearch.h @@ -3,7 +3,7 @@ #pragma once #include <bpf_arena_common.h> -__noinline int bpf_arena_strlen(const char __arena *s __arg_arena) +__noinline int bpf_arena_strlen(const char __arena *s) { const char __arena *sc; @@ -40,7 +40,7 @@ __noinline int bpf_arena_strlen(const char __arena *s __arg_arena) * * An opening bracket without a matching close is matched literally. */ -__noinline bool glob_match(char const __arena *pat __arg_arena, char const __arena *str __arg_arena) +__noinline bool glob_match(char const __arena *pat, char const __arena *str) { /* * Backtrack to previous * on mismatch and retry starting one diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h index 445be3c4edec..82aafe879fae 100644 --- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h +++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h @@ -38,7 +38,7 @@ #define cast_kern(ptr) /* nop for bpf prog. emitted by LLVM */ #define cast_user(ptr) /* nop for bpf prog. emitted by LLVM */ #else -#define __arena +#define __arena __attribute__((btf_type_tag("arena"))) #define __arena_global SEC(".addr_space.1") #define cast_kern(ptr) bpf_addr_space_cast(ptr, 0, 1) #define cast_user(ptr) bpf_addr_space_cast(ptr, 1, 0) @@ -54,7 +54,6 @@ void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym _ #else /* when compiled as user space code */ #define __arena -#define __arg_arena #define cast_kern(ptr) /* nop for user space */ #define cast_user(ptr) /* nop for user space */ __weak char arena[1]; diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h index 164638690a4d..ae6b72d15bb6 100644 --- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h +++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h @@ -16,10 +16,6 @@ #define EOPNOTSUPP 95 #define ETIMEDOUT 110 -#ifndef __arena -#define __arena __attribute__((address_space(1))) -#endif - extern unsigned long CONFIG_NR_CPUS __kconfig; /* @@ -246,7 +242,7 @@ static __always_inline int arena_spin_trylock(arena_spinlock_t __arena *lock) } __noinline __weak -int arena_spin_lock_slowpath(arena_spinlock_t __arena __arg_arena *lock, u32 val) +int arena_spin_lock_slowpath(arena_spinlock_t __arena *lock, u32 val) { struct arena_mcs_spinlock __arena *prev, *next, *node0, *node; int ret = -ETIMEDOUT; diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/asan.h b/tools/testing/selftests/bpf/libarena/include/libarena/asan.h index eb9fc69d9eb0..900267159292 100644 --- a/tools/testing/selftests/bpf/libarena/include/libarena/asan.h +++ b/tools/testing/selftests/bpf/libarena/include/libarena/asan.h @@ -25,12 +25,10 @@ extern volatile bool asan_report_once; #ifdef BPF_ARENA_ASAN -typedef s8 __arena s8a; - static inline -s8a *mem_to_shadow(void __arena __arg_arena *addr) +s8 __arena *mem_to_shadow(void __arena *addr) { - return (s8a *)(((u32)(u64)addr >> ASAN_SHADOW_SHIFT) + + return (s8 __arena *)(((u32)(u64)addr >> ASAN_SHADOW_SHIFT) + __asan_shadow_memory_dynamic_address); } diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h index 00e2437128ef..4d57fc1b5c26 100644 --- a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h +++ b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h @@ -2,12 +2,6 @@ /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #pragma once -struct buddy_chunk; -typedef struct buddy_chunk __arena buddy_chunk_t; - -struct buddy_header; -typedef struct buddy_header __arena buddy_header_t; - enum buddy_consts { /* * Minimum allocation is 1 << BUDDY_MIN_ALLOC_SHIFT. @@ -68,25 +62,21 @@ struct buddy_chunk { u8 allocated[BUDDY_CHUNK_ITEMS / 8]; /* Freelists for O(1) allocation. */ u64 freelists[BUDDY_CHUNK_NUM_ORDERS]; - buddy_chunk_t *next; + struct buddy_chunk __arena *next; }; struct buddy { - buddy_chunk_t *first_chunk; /* Pointer to the chunk linked list. */ + struct buddy_chunk __arena *first_chunk; /* Pointer to the chunk linked list. */ arena_spinlock_t lock; /* Allocator lock */ u64 vaddr; /* Allocation into reserved vaddr */ }; -typedef struct buddy __arena buddy_t; - #ifdef __BPF__ -int buddy_init(buddy_t *buddy); -int buddy_destroy(buddy_t *buddy); -int buddy_free_internal(buddy_t *buddy, u64 free); -#define buddy_free(buddy, ptr) buddy_free_internal((buddy), (u64)(ptr)) -u64 buddy_alloc_internal(buddy_t *buddy, size_t size); +int buddy_init(struct buddy __arena *buddy); +int buddy_destroy(struct buddy __arena *buddy); +int buddy_free(struct buddy __arena *buddy, void __arena *free); +u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size); #define buddy_alloc(alloc, size) ((void __arena *)buddy_alloc_internal((alloc), (size))) - #endif /* __BPF__ */ diff --git a/tools/testing/selftests/bpf/libarena/selftests/st_asan_buddy.bpf.c b/tools/testing/selftests/bpf/libarena/selftests/st_asan_buddy.bpf.c index 97acd50ffa5c..686caba2c643 100644 --- a/tools/testing/selftests/bpf/libarena/selftests/st_asan_buddy.bpf.c +++ b/tools/testing/selftests/bpf/libarena/selftests/st_asan_buddy.bpf.c @@ -8,7 +8,7 @@ /* Required for parsing the ASAN call stacks. */ #include "test_progs_compat.h" -extern buddy_t buddy; +extern struct buddy __arena buddy; #ifdef BPF_ARENA_ASAN @@ -54,7 +54,7 @@ static __always_inline int asan_test_buddy_oob_single(size_t alloc_size) * Factored out because asan_validate_addr is complex enough to cause * verification failures if verified with the rest of asan_test_buddy_uaf_single. */ -__weak int asan_test_buddy_byte(u8 __arena __arg_arena *mem, int i, bool freed) +__weak int asan_test_buddy_byte(u8 __arena *mem, int i, bool freed) { int ret; diff --git a/tools/testing/selftests/bpf/libarena/selftests/st_asan_common.h b/tools/testing/selftests/bpf/libarena/selftests/st_asan_common.h index 1d3edc4372ac..34a7918cb4cf 100644 --- a/tools/testing/selftests/bpf/libarena/selftests/st_asan_common.h +++ b/tools/testing/selftests/bpf/libarena/selftests/st_asan_common.h @@ -9,7 +9,7 @@ static inline void print_asan_map_state(void __arena *addr) { arena_stdout("%s:%d ASAN %p -> (val: %x gran: %x set: [%s])", __func__, __LINE__, addr, - *(s8a *)(addr), ASAN_GRANULE(addr), + *(s8 __arena *)(addr), ASAN_GRANULE(addr), asan_shadow_set(addr) ? "yes" : "no"); } diff --git a/tools/testing/selftests/bpf/libarena/selftests/st_buddy.bpf.c b/tools/testing/selftests/bpf/libarena/selftests/st_buddy.bpf.c index 79e6f0baabfe..b45a306816c0 100644 --- a/tools/testing/selftests/bpf/libarena/selftests/st_buddy.bpf.c +++ b/tools/testing/selftests/bpf/libarena/selftests/st_buddy.bpf.c @@ -6,7 +6,7 @@ #include <libarena/asan.h> #include <libarena/buddy.h> -extern buddy_t buddy; +extern struct buddy __arena buddy; struct segarr_entry { u8 __arena *block; diff --git a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c index 64c5b990086c..5135d5c72a46 100644 --- a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c +++ b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c @@ -110,7 +110,7 @@ volatile bool asan_report_once = false; * to exit due to a missing implementation. Provide a simple implementation * just for memset to use it for poisoning/unpoisoning the map. */ -__weak int asan_memset(s8a __arg_arena *dst, s8 val, size_t size) +__weak int asan_memset(s8 __arena *dst, s8 val, size_t size) { size_t i; @@ -121,9 +121,9 @@ __weak int asan_memset(s8a __arg_arena *dst, s8 val, size_t size) } /* Validate a 1-byte access, always within a single byte. */ -static __always_inline bool memory_is_poisoned_1(s8a *addr) +static __always_inline bool memory_is_poisoned_1(s8 __arena *addr) { - s8 shadow_value = *(s8a *)mem_to_shadow(addr); + s8 shadow_value = *(s8 __arena *)mem_to_shadow(addr); /* Byte is 0, access is valid. */ if (likely(!shadow_value)) @@ -139,7 +139,7 @@ static __always_inline bool memory_is_poisoned_1(s8a *addr) } /* Validate a 2- 4-, 8-byte access, shadow spans up to 2 bytes. */ -static __always_inline bool memory_is_poisoned_2_4_8(s8a *addr, u64 size) +static __always_inline bool memory_is_poisoned_2_4_8(s8 __arena *addr, u64 size) { u64 end = (u64)addr + size - 1; @@ -148,17 +148,17 @@ static __always_inline bool memory_is_poisoned_2_4_8(s8a *addr, u64 size) * overflow above ASAN_GRANULE). */ if (likely(ASAN_GRANULE(end) >= size - 1)) - return memory_is_poisoned_1((s8a *)end); + return memory_is_poisoned_1((s8 __arena *)end); /* * Otherwise first byte must be fully unpoisoned, and second byte * must be unpoisoned up to the end of the accessed region. */ - return *(s8a *)mem_to_shadow(addr) || memory_is_poisoned_1((s8a *)end); + return *(s8 __arena *)mem_to_shadow(addr) || memory_is_poisoned_1((s8 __arena *)end); } -__weak bool asan_shadow_set(void __arena __arg_arena *addr) +__weak bool asan_shadow_set(void __arena *addr) { return memory_is_poisoned_1(addr); } @@ -166,7 +166,7 @@ __weak bool asan_shadow_set(void __arena __arg_arena *addr) static __always_inline u64 first_nonzero_byte(u64 addr, size_t size) { while (size && can_loop) { - if (unlikely(*(s8a *)addr)) + if (unlikely(*(s8 __arena *)addr)) return addr; addr += 1; size -= 1; @@ -175,7 +175,7 @@ static __always_inline u64 first_nonzero_byte(u64 addr, size_t size) return SHADOW_ALL_ZEROES; } -static __always_inline bool memory_is_poisoned_n(s8a *addr, u64 size) +static __always_inline bool memory_is_poisoned_n(s8 __arena *addr, u64 size) { u64 ret; u64 start; @@ -189,10 +189,10 @@ static __always_inline bool memory_is_poisoned_n(s8a *addr, u64 size) if (likely(ret == SHADOW_ALL_ZEROES)) return false; - return unlikely(ret != end || ASAN_GRANULE(addr + size - 1) >= *(s8a *)end); + return unlikely(ret != end || ASAN_GRANULE(addr + size - 1) >= *(s8 __arena *)end); } -__weak int asan_report(s8a __arg_arena *addr, size_t sz, u32 flags) +__weak int asan_report(s8 __arena *addr, size_t sz, u32 flags) { u32 reported = __sync_val_compare_and_swap(&asan_reported, false, true); @@ -211,7 +211,7 @@ __weak int asan_report(s8a __arg_arena *addr, size_t sz, u32 flags) return 0; } -static __always_inline bool check_asan_args(s8a *addr, size_t size, +static __always_inline bool check_asan_args(s8 __arena *addr, size_t size, bool *result) { bool valid = true; @@ -253,7 +253,7 @@ static __always_inline bool check_asan_args(s8a *addr, size_t size, static __always_inline bool check_region_inline(intptr_t ptr, size_t size, u32 flags) { - s8a *addr = (s8a *)(u64)ptr; + s8 __arena *addr = (s8 __arena *)(u64)ptr; bool is_poisoned, is_valid; if (check_asan_args(addr, size, &is_valid)) { @@ -305,19 +305,19 @@ static __always_inline bool check_region_inline(intptr_t ptr, size_t size, } \ __hidden void __asan_report_store##size(intptr_t addr) \ { \ - asan_report((s8a *)addr, size, ASAN_WRITE); \ + asan_report((s8 __arena *)addr, size, ASAN_WRITE); \ } \ __hidden void __asan_report_store##size##_noabort(intptr_t addr) \ { \ - asan_report((s8a *)addr, size, ASAN_WRITE); \ + asan_report((s8 __arena *)addr, size, ASAN_WRITE); \ } \ __hidden void __asan_report_load##size(intptr_t addr) \ { \ - asan_report((s8a *)addr, size, ASAN_READ); \ + asan_report((s8 __arena *)addr, size, ASAN_READ); \ } \ __hidden void __asan_report_load##size##_noabort(intptr_t addr) \ { \ - asan_report((s8a *)addr, size, ASAN_READ); \ + asan_report((s8 __arena *)addr, size, ASAN_READ); \ } DEFINE_ASAN_LOAD_STORE(1); @@ -385,7 +385,7 @@ void *__asan_memset(void *p, int c, size_t n) */ __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size) { - s8a *shadow; + s8 __arena *shadow; size_t len; /* @@ -443,7 +443,7 @@ __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size) __hidden __noasan int asan_unpoison(void __arena *addr, size_t size) { size_t partial = size & ASAN_GRANULE_MASK; - s8a *shadow; + s8 __arena *shadow; size_t len; /* diff --git a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c index 865e00803daa..f4ed4c3abb4b 100644 --- a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c +++ b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c @@ -45,12 +45,12 @@ enum { BUDDY_CHUNK_PAGES = BUDDY_CHUNK_BYTES / __PAGE_SIZE }; -static inline int buddy_lock(buddy_t *buddy) +static inline int buddy_lock(struct buddy __arena *buddy) { return arena_spin_lock(&buddy->lock); } -static inline void buddy_unlock(buddy_t *buddy) +static inline void buddy_unlock(struct buddy __arena *buddy) { arena_spin_unlock(&buddy->lock); } @@ -61,7 +61,7 @@ static inline void buddy_unlock(buddy_t *buddy) * page alloc kfuncs do not support aligning to a boundary (in this * case 1 MiB, see buddy.h on how this is derived). */ -static int buddy_reserve_arena_vaddr(buddy_t *buddy) +static int buddy_reserve_arena_vaddr(struct buddy __arena *buddy) { buddy->vaddr = 0; @@ -73,7 +73,7 @@ static int buddy_reserve_arena_vaddr(buddy_t *buddy) /* * Free up any unused address space. Used only during teardown. */ -static void buddy_unreserve_arena_vaddr(buddy_t *buddy) +static void buddy_unreserve_arena_vaddr(struct buddy __arena *buddy) { bpf_arena_free_pages( &arena, (void __arena *)(BUDDY_VADDR_OFFSET + buddy->vaddr), @@ -94,7 +94,7 @@ static void buddy_unreserve_arena_vaddr(buddy_t *buddy) * However, bump allocation must still be atomic because this function * is called without the buddy lock from multiple threads concurrently. */ -__weak int buddy_alloc_arena_vaddr(buddy_t __arg_arena *buddy, u64 *vaddrp) +__weak int buddy_alloc_arena_vaddr(struct buddy __arena *buddy, u64 *vaddrp) { u64 vaddr, old, new; @@ -134,7 +134,7 @@ static u64 arena_next_pow2(__u64 n) } __weak -int idx_set_allocated(buddy_chunk_t __arg_arena *chunk, u64 idx, bool allocated) +int idx_set_allocated(struct buddy_chunk __arena *chunk, u64 idx, bool allocated) { bool already_allocated; @@ -160,7 +160,7 @@ int idx_set_allocated(buddy_chunk_t __arg_arena *chunk, u64 idx, bool allocated) return 0; } -static int idx_is_allocated(buddy_chunk_t *chunk, u64 idx, bool *allocated) +static int idx_is_allocated(struct buddy_chunk __arena *chunk, u64 idx, bool *allocated) { if (unlikely(idx >= BUDDY_CHUNK_ITEMS)) { arena_stderr("getting state of invalid idx (%llu, max %d)\n", idx, @@ -173,7 +173,7 @@ static int idx_is_allocated(buddy_chunk_t *chunk, u64 idx, bool *allocated) } __weak -int idx_set_order(buddy_chunk_t __arg_arena *chunk, u64 idx, u8 order) +int idx_set_order(struct buddy_chunk __arena *chunk, u64 idx, u8 order) { u8 prev_order; @@ -206,7 +206,7 @@ int idx_set_order(buddy_chunk_t __arg_arena *chunk, u64 idx, u8 order) return 0; } -static u8 idx_get_order(buddy_chunk_t *chunk, u64 idx) +static u8 idx_get_order(struct buddy_chunk __arena *chunk, u64 idx) { u8 result; @@ -223,7 +223,7 @@ static u8 idx_get_order(buddy_chunk_t *chunk, u64 idx) return (idx & 0x1) ? (result & 0xf) : (result >> 4); } -static void __arena *idx_to_addr(buddy_chunk_t *chunk, size_t idx) +static void __arena *idx_to_addr(struct buddy_chunk __arena *chunk, size_t idx) { u64 address; @@ -246,7 +246,7 @@ static void __arena *idx_to_addr(buddy_chunk_t *chunk, size_t idx) return (void __arena *)address; } -static buddy_header_t *idx_to_header(buddy_chunk_t *chunk, size_t idx) +static struct buddy_header __arena *idx_to_header(struct buddy_chunk __arena *chunk, size_t idx) { bool allocated; u64 address; @@ -283,13 +283,13 @@ static buddy_header_t *idx_to_header(buddy_chunk_t *chunk, size_t idx) * less probable. */ - return (buddy_header_t *)(address + BUDDY_HEADER_OFF); + return (struct buddy_header __arena *)(address + BUDDY_HEADER_OFF); } -static void header_add_freelist(buddy_chunk_t *chunk, buddy_header_t *header, +static void header_add_freelist(struct buddy_chunk __arena *chunk, struct buddy_header __arena *header, u64 idx, u8 order) { - buddy_header_t *tmp_header; + struct buddy_header __arena *tmp_header; idx_set_order(chunk, idx, order); @@ -304,10 +304,10 @@ static void header_add_freelist(buddy_chunk_t *chunk, buddy_header_t *header, chunk->freelists[order] = idx; } -static void header_remove_freelist(buddy_chunk_t *chunk, - buddy_header_t *header, u8 order) +static void header_remove_freelist(struct buddy_chunk __arena *chunk, + struct buddy_header __arena *header, u8 order) { - buddy_header_t *tmp_header; + struct buddy_header __arena *tmp_header; if (header->prev_index != BUDDY_CHUNK_ITEMS) { tmp_header = idx_to_header(chunk, header->prev_index); @@ -356,10 +356,10 @@ static u64 size_to_order(size_t size) } __weak -int add_leftovers_to_freelist(buddy_chunk_t __arg_arena *chunk, u32 cur_idx, +int add_leftovers_to_freelist(struct buddy_chunk __arena *chunk, u32 cur_idx, u64 min_order, u64 max_order) { - buddy_header_t *header; + struct buddy_header __arena *header; u64 ord; u32 idx; @@ -381,10 +381,10 @@ int add_leftovers_to_freelist(buddy_chunk_t __arg_arena *chunk, u32 cur_idx, return 0; } -static buddy_chunk_t *buddy_chunk_get(buddy_t *buddy) +static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy) { u64 order, ord, min_order, max_order; - buddy_chunk_t *chunk; + struct buddy_chunk __arena *chunk; size_t left; int power2; u64 vaddr; @@ -561,9 +561,9 @@ static buddy_chunk_t *buddy_chunk_get(buddy_t *buddy) return chunk; } -__weak int buddy_init(buddy_t __arg_arena *buddy) +__weak int buddy_init(struct buddy __arena *buddy) { - buddy_chunk_t *chunk; + struct buddy_chunk __arena *chunk; int ret; if (!asan_ready()) @@ -602,9 +602,9 @@ __weak int buddy_init(buddy_t __arg_arena *buddy) * We do not take a lock because we are freeing arena pages, and nobody should * be using the allocator at that point in the execution. */ -__weak int buddy_destroy(buddy_t __arg_arena *buddy) +__weak int buddy_destroy(struct buddy __arena *buddy) { - buddy_chunk_t *chunk, *next; + struct buddy_chunk __arena *chunk, *next; if (!buddy) return -EINVAL; @@ -631,9 +631,9 @@ __weak int buddy_destroy(buddy_t __arg_arena *buddy) return 0; } -__weak u64 buddy_chunk_alloc(buddy_chunk_t __arg_arena *chunk, int order_req) +__weak u64 buddy_chunk_alloc(struct buddy_chunk __arena *chunk, int order_req) { - buddy_header_t *header, *tmp_header, *next_header; + struct buddy_header __arena *header, *tmp_header, *next_header; u32 idx, tmpidx, retidx; u64 address; u64 order = 0; @@ -709,9 +709,9 @@ __weak u64 buddy_chunk_alloc(buddy_chunk_t __arg_arena *chunk, int order_req) } /* Scan the existing chunks for available memory. */ -static u64 buddy_alloc_from_existing_chunks(buddy_t *buddy, int order) +static u64 buddy_alloc_from_existing_chunks(struct buddy __arena *buddy, int order) { - buddy_chunk_t *chunk; + struct buddy_chunk __arena *chunk; u64 address; for (chunk = buddy->first_chunk; chunk != NULL && can_loop; @@ -728,7 +728,7 @@ static u64 buddy_alloc_from_existing_chunks(buddy_t *buddy, int order) * Try an allocation from a newly allocated chunk. Also * incorporate the chunk into the linked list. */ -static u64 buddy_alloc_from_new_chunk(buddy_t *buddy, buddy_chunk_t *chunk, int order) +static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_chunk __arena *chunk, int order) { u64 address; @@ -750,10 +750,10 @@ static u64 buddy_alloc_from_new_chunk(buddy_t *buddy, buddy_chunk_t *chunk, int return (u64)address; } __weak -u64 buddy_alloc_internal(buddy_t __arg_arena *buddy, size_t size) +u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size) { - buddy_chunk_t *chunk; u64 address = (u64)NULL; + struct buddy_chunk __arena *chunk; int order; if (!buddy) @@ -788,20 +788,20 @@ u64 buddy_alloc_internal(buddy_t __arg_arena *buddy, size_t size) * data is smaller than the header, we must poison any * unused bytes that were part of the header. */ - if (size < BUDDY_HEADER_OFF + sizeof(buddy_header_t)) - asan_poison((u8 __arena *)address + BUDDY_HEADER_OFF, - BUDDY_POISONED, sizeof(buddy_header_t)); + if (size < BUDDY_HEADER_OFF + sizeof(struct buddy_header __arena)) + asan_poison((u8 __arena *)address + BUDDY_HEADER_OFF, BUDDY_POISONED, + sizeof(struct buddy_header __arena)); asan_unpoison((u8 __arena *)address, size); return address; } -static __always_inline int buddy_free_unlocked(buddy_t *buddy, u64 addr) +static __always_inline int buddy_free_unlocked(struct buddy __arena *buddy, u64 addr) { - buddy_header_t *header, *buddy_header; + struct buddy_header __arena *header, *buddy_header; u64 idx, buddy_idx, tmp_idx; - buddy_chunk_t *chunk; + struct buddy_chunk __arena *chunk; bool allocated; u8 order; int ret; @@ -878,7 +878,7 @@ static __always_inline int buddy_free_unlocked(buddy_t *buddy, u64 addr) return 0; } -__weak int buddy_free_internal(buddy_t __arg_arena *buddy, u64 addr) +__weak int buddy_free(struct buddy __arena *buddy, void __arena *addr) { int ret; @@ -893,7 +893,7 @@ __weak int buddy_free_internal(buddy_t __arg_arena *buddy, u64 addr) if (ret) return ret; - ret = buddy_free_unlocked(buddy, addr); + ret = buddy_free_unlocked(buddy, (u64)addr); buddy_unlock(buddy); diff --git a/tools/testing/selftests/bpf/libarena/src/common.bpf.c b/tools/testing/selftests/bpf/libarena/src/common.bpf.c index 544bf9e1cb38..ec9de29e6f3e 100644 --- a/tools/testing/selftests/bpf/libarena/src/common.bpf.c +++ b/tools/testing/selftests/bpf/libarena/src/common.bpf.c @@ -6,7 +6,7 @@ const volatile u32 zero = 0; -buddy_t buddy; +struct buddy __arena buddy; int arena_fls(__u64 word) { @@ -43,9 +43,9 @@ __weak u64 arena_malloc_internal(size_t size) return buddy_alloc_internal(&buddy, size); } -__weak void arena_free(void __arg_arena __arena *ptr) +__weak void arena_free(void __arena *ptr) { - buddy_free_internal(&buddy, (u64)ptr); + buddy_free(&buddy, ptr); } diff --git a/tools/testing/selftests/bpf/progs/arena_spin_lock.c b/tools/testing/selftests/bpf/progs/arena_spin_lock.c index 7236d92d382f..cf7cda79c16c 100644 --- a/tools/testing/selftests/bpf/progs/arena_spin_lock.c +++ b/tools/testing/selftests/bpf/progs/arena_spin_lock.c @@ -4,6 +4,7 @@ #include <bpf/bpf_tracing.h> #include <bpf/bpf_helpers.h> #include "bpf_misc.h" +#include <bpf_arena_common.h> #include <bpf_arena_spin_lock.h> struct { -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase 2026-06-02 0:41 ` [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase Emil Tsalapatis @ 2026-06-02 1:31 ` sashiko-bot 0 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-06-02 1:31 UTC (permalink / raw) To: Emil Tsalapatis; +Cc: bpf Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] Leftover `__arena` typedefs in header files cause their corresponding pointer types to silently drop the newly introduced `btf_type_tag("arena")` annotation. - [Low] The removal of the fallback `__arena` macro from `bpf_arena_spin_lock.h` breaks the header's self-containment. -- commit b79db788bee1871973404a0b3039c644cb84cce8 Author: Emil Tsalapatis <emil@etsalapatis.com> selftests/bpf: Remove __arg_arena from the codebase This commit removes the __arg_arena annotation by subsuming it into __arena, which now includes a BTF type tag. To avoid compiler issues with BTF type tags on non-pointer types, typedefs previously used for __arena are replaced with direct pointer annotations. > diff --git a/tools/testing/selftests/bpf/bpf_arena_htab.h b/tools/testing/selftests/bpf/bpf_arena_htab.h > index acc01a876668e..d7ba86362d862 100644 > --- a/tools/testing/selftests/bpf/bpf_arena_htab.h > +++ b/tools/testing/selftests/bpf/bpf_arena_htab.h > @@ -14,9 +14,8 @@ struct htab { > htab_bucket_t *buckets; > int n_buckets; > }; > -typedef struct htab __arena htab_t; > > -static inline htab_bucket_t *__select_bucket(htab_t *htab, __u32 hash) > +static inline htab_bucket_t *__select_bucket(struct htab __arena *htab, __u32 hash) > { [Severity: Medium] Since __arena now includes __attribute__((btf_type_tag("arena"))), applying it to a non-pointer typedef causes the compiler to drop the BTF type tag on pointers declared with that type. This patch removes htab_t to fix this, but does this miss other __arena typedefs like htab_bucket_t and hashtab_elem_t in this file, or arena_list_node_t and arena_list_head_t in bpf_arena_list.h? Will pointers declared with those leftover typedefs silently drop their BTF type tag annotations? [ ... ] > diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h > index 164638690a4df..ae6b72d15bb67 100644 > --- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h > +++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h > @@ -16,10 +16,6 @@ > #define EOPNOTSUPP 95 > #define ETIMEDOUT 110 > > -#ifndef __arena > -#define __arena __attribute__((address_space(1))) > -#endif > - > extern unsigned long CONFIG_NR_CPUS __kconfig; [Severity: Low] Does removing this fallback definition without including bpf_arena_common.h break the self-containment of this header? Any source file including bpf_arena_spin_lock.h on its own might now fail to compile due to an undeclared __arena identifier. [ ... ] > diff --git a/tools/testing/selftests/bpf/progs/arena_spin_lock.c b/tools/testing/selftests/bpf/progs/arena_spin_lock.c > index 7236d92d382f1..cf7cda79c16c0 100644 > --- a/tools/testing/selftests/bpf/progs/arena_spin_lock.c > +++ b/tools/testing/selftests/bpf/progs/arena_spin_lock.c > @@ -4,6 +4,7 @@ > #include <bpf/bpf_tracing.h> > #include <bpf/bpf_helpers.h> > #include "bpf_misc.h" > +#include <bpf_arena_common.h> > #include <bpf_arena_spin_lock.h> [Severity: Low] This isn't a bug in this file, but this looks like a workaround for the broken header self-containment mentioned above. Should bpf_arena_common.h be included directly in bpf_arena_spin_lock.h instead? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602004120.17087-1-emil@etsalapatis.com?part=4 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis ` (3 preceding siblings ...) 2026-06-02 0:41 ` [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 1:45 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 6/6] selftests/bpf: Add tests for the new type-tag based __arena identifier Emil Tsalapatis 2026-06-02 2:00 ` [PATCH bpf-next v3 0/6] Minimize annotations for arena programs patchwork-bot+netdevbpf 6 siblings, 1 reply; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis Now that the __arena annotation includes a BTF type tag, and the verifier can identify arena pointers at BTF loading time, return arena pointers as their true type instead of casting to u64. Remove the preprocessor typecast wrappers used to hide this from the caller. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- .../bpf/libarena/include/libarena/buddy.h | 3 +-- .../bpf/libarena/include/libarena/common.h | 3 +-- .../selftests/bpf/libarena/src/buddy.bpf.c | 20 +++++++++---------- .../selftests/bpf/libarena/src/common.bpf.c | 4 ++-- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h index 4d57fc1b5c26..528c69a1f38e 100644 --- a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h +++ b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h @@ -76,7 +76,6 @@ struct buddy { int buddy_init(struct buddy __arena *buddy); int buddy_destroy(struct buddy __arena *buddy); int buddy_free(struct buddy __arena *buddy, void __arena *free); -u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size); -#define buddy_alloc(alloc, size) ((void __arena *)buddy_alloc_internal((alloc), (size))) +void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size); #endif /* __BPF__ */ diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/common.h b/tools/testing/selftests/bpf/libarena/include/libarena/common.h index ca1a6c1d6477..a3eb1641ac36 100644 --- a/tools/testing/selftests/bpf/libarena/include/libarena/common.h +++ b/tools/testing/selftests/bpf/libarena/include/libarena/common.h @@ -48,8 +48,7 @@ extern volatile u64 asan_violated; int arena_fls(__u64 word); -u64 arena_malloc_internal(size_t size); -#define arena_malloc(size) ((void __arena *)arena_malloc_internal((size))) +void __arena *arena_malloc(size_t size); void arena_free(void __arena *ptr); /* diff --git a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c index f4ed4c3abb4b..c674ee5cfcc1 100644 --- a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c +++ b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c @@ -750,25 +750,25 @@ static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_ return (u64)address; } __weak -u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size) +void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size) { - u64 address = (u64)NULL; + void __arena *address = NULL; struct buddy_chunk __arena *chunk; int order; if (!buddy) - return (u64)NULL; + return NULL; order = size_to_order(size); if (order >= BUDDY_CHUNK_NUM_ORDERS || order < 0) { arena_stderr("invalid order %d (sz %lu)\n", order, size); - return (u64)NULL; + return NULL; } if (buddy_lock(buddy)) - return (u64)NULL; + return NULL; - address = buddy_alloc_from_existing_chunks(buddy, order); + address = (u8 __arena *)buddy_alloc_from_existing_chunks(buddy, order); buddy_unlock(buddy); if (address) goto done; @@ -776,12 +776,12 @@ u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size) /* Get a new chunk. */ chunk = buddy_chunk_get(buddy); if (chunk) - address = buddy_alloc_from_new_chunk(buddy, chunk, order); + address = (u8 __arena *)buddy_alloc_from_new_chunk(buddy, chunk, order); done: /* If we failed to allocate memory, return NULL. */ if (!address) - return (u64)NULL; + return NULL; /* * Unpoison exactly the amount of bytes requested. If the @@ -789,10 +789,10 @@ u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size) * unused bytes that were part of the header. */ if (size < BUDDY_HEADER_OFF + sizeof(struct buddy_header __arena)) - asan_poison((u8 __arena *)address + BUDDY_HEADER_OFF, BUDDY_POISONED, + asan_poison(address + BUDDY_HEADER_OFF, BUDDY_POISONED, sizeof(struct buddy_header __arena)); - asan_unpoison((u8 __arena *)address, size); + asan_unpoison(address, size); return address; } diff --git a/tools/testing/selftests/bpf/libarena/src/common.bpf.c b/tools/testing/selftests/bpf/libarena/src/common.bpf.c index ec9de29e6f3e..50be57213dfb 100644 --- a/tools/testing/selftests/bpf/libarena/src/common.bpf.c +++ b/tools/testing/selftests/bpf/libarena/src/common.bpf.c @@ -38,9 +38,9 @@ __weak int arena_buddy_reset(void) return buddy_init(&buddy); } -__weak u64 arena_malloc_internal(size_t size) +__weak void __arena *arena_malloc(size_t size) { - return buddy_alloc_internal(&buddy, size); + return buddy_alloc(&buddy, size); } __weak void arena_free(void __arena *ptr) -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions 2026-06-02 0:41 ` [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions Emil Tsalapatis @ 2026-06-02 1:45 ` sashiko-bot 0 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-06-02 1:45 UTC (permalink / raw) To: Emil Tsalapatis; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Returning `void __arena *` natively from global subprograms causes verifier rejection due to the omission of the `addr_space_cast` instruction. -- commit cebc1ff5de4bdc037926c8cf353b253eb29e111c Author: Emil Tsalapatis <emil@etsalapatis.com> selftests/bpf: libarena: Directly return arena pointers from functions This commit updates the libarena functions to directly return arena pointers instead of returning u64 and relying on preprocessor macros for type casting to hide the conversion from the caller. > diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h > index 4d57fc1b5c26f..528c69a1f38e0 100644 > --- a/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h > +++ b/tools/testing/selftests/bpf/libarena/include/libarena/buddy.h > @@ -76,7 +76,6 @@ struct buddy { > int buddy_init(struct buddy __arena *buddy); > int buddy_destroy(struct buddy __arena *buddy); > int buddy_free(struct buddy __arena *buddy, void __arena *free); > -u64 buddy_alloc_internal(struct buddy __arena *buddy, size_t size); > -#define buddy_alloc(alloc, size) ((void __arena *)buddy_alloc_internal((alloc), (size))) > +void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size); [Severity: High] Does changing the return type to void __arena * break the BPF verifier for programs calling these functions? Since this change removes the explicit C-style cast from a u64, Clang will no longer emit the BPF_ADDR_SPACE_CAST instruction because both sides of the assignment share the same address space. However, in kernel/bpf/verifier.c:check_func_call(), the verifier still forces the return register (R0) of all global subprograms to SCALAR_VALUE: if (!subprog_returns_void(env, subprog)) { mark_reg_unknown(env, caller->regs, BPF_REG_0); caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; } Without the addr_space_cast instruction to upgrade the SCALAR_VALUE back to a PTR_TO_ARENA, will subsequent memory accesses using this returned pointer be rejected by the verifier with an "invalid mem access 'scalar'" error? It appears check_func_call() might need to be updated to populate the caller's R0 state with PTR_TO_ARENA based on the BTF return type before this test can safely rely on native arena pointer returns. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602004120.17087-1-emil@etsalapatis.com?part=5 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH bpf-next v3 6/6] selftests/bpf: Add tests for the new type-tag based __arena identifier 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis ` (4 preceding siblings ...) 2026-06-02 0:41 ` [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions Emil Tsalapatis @ 2026-06-02 0:41 ` Emil Tsalapatis 2026-06-02 2:00 ` [PATCH bpf-next v3 0/6] Minimize annotations for arena programs patchwork-bot+netdevbpf 6 siblings, 0 replies; 14+ messages in thread From: Emil Tsalapatis @ 2026-06-02 0:41 UTC (permalink / raw) To: bpf Cc: ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski, Emil Tsalapatis Add selftests that combine the new type-based __arena identifier with the volatile qualifier both in functions' arguments and return values. This way we test both that they are recognized as arena arguments and that they are not sensitive to the position they are placed in the type compared to other qualifiers. Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> --- .../selftests/bpf/progs/verifier_arena.c | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c index 89d72c8d756a..df0e22d1a29b 100644 --- a/tools/testing/selftests/bpf/progs/verifier_arena.c +++ b/tools/testing/selftests/bpf/progs/verifier_arena.c @@ -607,4 +607,71 @@ int non_arena_ptr_add_to_arena_ptr(void *ctx) #endif +static __noinline +u32 __arena *check_arena_arg_nonglobal(u32 __arena *arg) +{ + volatile u32 val = *arg; + + *arg = val + 1; + + return arg; +} + +__weak +u32 __arena *check_arena_arg_global(u32 __arena *arg) +{ + volatile u32 val = *arg; + + *arg = val + 1; + + return arg; +} + +__weak +u32 volatile __arena *check_arena_arg_quals1(u32 volatile __arena *arg1, u32 __arena volatile *arg2) +{ + *arg1 = *arg1 + 1; + *arg2 = *arg1 + 1; + + return arg2; +} + +__weak +u32 __arena volatile *check_arena_arg_quals2(u32 volatile __arena *arg1, u32 __arena volatile *arg2) +{ + *arg1 = *arg1 + 1; + *arg2 = *arg2 + 1; + + return arg2; +} + +SEC("syscall") +__success __retval(0) +int check_arena_arg_ret(void *ctx) +{ + u32 __arena *page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); + u32 __arena *arg = page; + u32 __arena volatile *arg1; + u32 __arena volatile *ret1; + u32 volatile __arena *arg2; + u32 volatile __arena *ret2; + + if (!arg) + return 1; + + /* Make sure we use {arg, ret}{1, 2}. */ + + arg = check_arena_arg_nonglobal(page); + arg = check_arena_arg_global(arg); + + arg1 = arg2 = page; + ret1 = check_arena_arg_quals1(arg1, arg2); + ret2 = check_arena_arg_quals2(arg1, arg2); + + if (!(*ret1 ||*ret2)) + return -EINVAL; + + return 0; +} + char _license[] SEC("license") = "GPL"; -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v3 0/6] Minimize annotations for arena programs 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis ` (5 preceding siblings ...) 2026-06-02 0:41 ` [PATCH bpf-next v3 6/6] selftests/bpf: Add tests for the new type-tag based __arena identifier Emil Tsalapatis @ 2026-06-02 2:00 ` patchwork-bot+netdevbpf 6 siblings, 0 replies; 14+ messages in thread From: patchwork-bot+netdevbpf @ 2026-06-02 2:00 UTC (permalink / raw) To: Emil Tsalapatis Cc: bpf, ast, andrii, memxor, daniel, eddyz87, song, mattbobrowski Hello: This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <ast@kernel.org>: On Mon, 1 Jun 2026 20:41:14 -0400 you wrote: > BPF programs must currently include code to address two limitations > of function signatures that include arena types. First, arena arguments > must be annotated with __arg_arena in the function signature in addition > to __arena. Second, it is currently not allowed to return an arena pointer > from a subprog, even though it is safe to do so. These limitations require > extra annotations and typecasts respectively, and have proven sources of > confusion to programmers. > > [...] Here is the summary with links: - [bpf-next,v3,1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier https://git.kernel.org/bpf/bpf-next/c/a0fa68d8ce75 - [bpf-next,v3,2/6] verifier: parse BTF type tags for function arguments https://git.kernel.org/bpf/bpf-next/c/5ab4bc67d818 - [bpf-next,v3,3/6] bpf: Allow subprogs to return arena pointers https://git.kernel.org/bpf/bpf-next/c/3e924e9272c8 - [bpf-next,v3,4/6] selftests/bpf: Remove __arg_arena from the codebase https://git.kernel.org/bpf/bpf-next/c/b9b23fe17611 - [bpf-next,v3,5/6] selftests/bpf: libarena: Directly return arena pointers from functions https://git.kernel.org/bpf/bpf-next/c/367e6e4a8173 - [bpf-next,v3,6/6] selftests/bpf: Add tests for the new type-tag based __arena identifier https://git.kernel.org/bpf/bpf-next/c/9fd5bf96ac4b You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-06-02 2:00 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-02 0:41 [PATCH bpf-next v3 0/6] Minimize annotations for arena programs Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 1/6] selftests/bpf: libarena: Add "arena" BTF type tag to __arena qualifier Emil Tsalapatis 2026-06-02 0:41 ` [PATCH bpf-next v3 2/6] verifier: parse BTF type tags for function arguments Emil Tsalapatis 2026-06-02 1:05 ` sashiko-bot 2026-06-02 1:05 ` Emil Tsalapatis 2026-06-02 1:26 ` bot+bpf-ci 2026-06-02 0:41 ` [PATCH bpf-next v3 3/6] bpf: Allow subprogs to return arena pointers Emil Tsalapatis 2026-06-02 1:20 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 4/6] selftests/bpf: Remove __arg_arena from the codebase Emil Tsalapatis 2026-06-02 1:31 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 5/6] selftests/bpf: libarena: Directly return arena pointers from functions Emil Tsalapatis 2026-06-02 1:45 ` sashiko-bot 2026-06-02 0:41 ` [PATCH bpf-next v3 6/6] selftests/bpf: Add tests for the new type-tag based __arena identifier Emil Tsalapatis 2026-06-02 2:00 ` [PATCH bpf-next v3 0/6] Minimize annotations for arena programs patchwork-bot+netdevbpf
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.