Skip to content

Commit 3e924e9

Browse files
etsalAlexei Starovoitov
authored andcommitted
bpf: Allow subprogs to return arena pointers
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> Link: https://lore.kernel.org/r/20260602004120.17087-4-emil@etsalapatis.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 5ab4bc6 commit 3e924e9

2 files changed

Lines changed: 54 additions & 16 deletions

File tree

kernel/bpf/btf.c

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7858,12 +7858,22 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env,
78587858

78597859
/* Find the first pointer type in the chain. */
78607860
t = btf_type_skip_modifiers(btf, type_id, NULL);
7861+
7862+
/*
7863+
* We currently reject type tags on non-pointer types,
7864+
* which neither LLVM nor GCC support anyway.
7865+
*/
78617866
if (!t || !btf_type_is_ptr(t))
78627867
return 0;
78637868

78647869
/* We got a pointer, get all associated type tags. */
7865-
t = btf_type_by_id(btf, t->type);
7866-
while (t && btf_type_is_type_tag(t)) {
7870+
for (t = btf_type_by_id(btf, t->type); t && btf_type_is_modifier(t);
7871+
t = btf_type_by_id(btf, t->type)) {
7872+
7873+
/* Skip non-type tag modifiers. */
7874+
if (!btf_type_is_type_tag(t))
7875+
continue;
7876+
78677877
const char *tag = __btf_name_by_offset(btf, t->name_off);
78687878

78697879
if (strcmp(tag, "arena") == 0) {
@@ -7873,13 +7883,39 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env,
78737883
tag);
78747884
return -EOPNOTSUPP;
78757885
}
7876-
7877-
t = btf_type_by_id(btf, t->type);
78787886
}
78797887

78807888
return 0;
78817889
}
78827890

7891+
/* Check whether the type is a valid return type. */
7892+
static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *btf,
7893+
const struct btf_type *t, int subprog)
7894+
{
7895+
u32 tags = 0;
7896+
int err;
7897+
7898+
err = btf_scan_type_tags(env, btf, t->type, &tags);
7899+
if (err)
7900+
return err;
7901+
7902+
t = btf_type_skip_modifiers(btf, t->type, NULL);
7903+
7904+
/*
7905+
* We allow all subprogs except for the main one to return any kind of arena pointer.
7906+
* General arena variables are not allowed, since it makes no sense to return by value
7907+
* a variable that's on the heap in the first place.
7908+
*/
7909+
if (subprog && (tags & ARG_TAG_ARENA) && btf_type_is_ptr(t))
7910+
return 0;
7911+
7912+
/* We always accept void or scalars. */
7913+
if (btf_type_is_void(t) || btf_type_is_int(t) || btf_is_any_enum(t))
7914+
return 0;
7915+
7916+
return -EOPNOTSUPP;
7917+
}
7918+
78837919
/* Process BTF of a function to produce high-level expectation of function
78847920
* arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
78857921
* is cached in subprog info for reuse.
@@ -7963,18 +7999,16 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
79637999
tname, nargs, MAX_BPF_FUNC_REG_ARGS);
79648000
return -EINVAL;
79658001
}
7966-
/* check that function is void or returns int, exception cb also requires this */
7967-
t = btf_type_by_id(btf, t->type);
7968-
while (btf_type_is_modifier(t))
7969-
t = btf_type_by_id(btf, t->type);
7970-
if (!btf_type_is_void(t) && !btf_type_is_int(t) && !btf_is_any_enum(t)) {
7971-
if (!is_global)
7972-
return -EINVAL;
7973-
bpf_log(log,
7974-
"Global function %s() return value not void or scalar. "
7975-
"Only those are supported.\n",
7976-
tname);
7977-
return -EINVAL;
8002+
8003+
err = btf_validate_return_type(env, btf, t, subprog);
8004+
if (err) {
8005+
if (is_global) {
8006+
bpf_log(log,
8007+
"Global function %s() return value not void or scalar. "
8008+
"Only those are supported.\n",
8009+
tname);
8010+
}
8011+
return err;
79788012
}
79798013

79808014
/* Convert BTF function arguments into verifier types.

kernel/bpf/verifier.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16503,6 +16503,10 @@ static int check_global_subprog_return_code(struct bpf_verifier_env *env)
1650316503
if (err)
1650416504
return err;
1650516505

16506+
/* Pointers to arena are safe to pass between subprograms. */
16507+
if (is_arena_reg(env, BPF_REG_0))
16508+
return 0;
16509+
1650616510
if (is_pointer_value(env, BPF_REG_0)) {
1650716511
verbose(env, "R%d leaks addr as return value\n", BPF_REG_0);
1650816512
return -EACCES;

0 commit comments

Comments
 (0)