summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmil Tsalapatis <emil@etsalapatis.com>2026-06-02 03:41:17 +0300
committerAlexei Starovoitov <ast@kernel.org>2026-06-02 04:42:33 +0300
commit3e924e9272c80939677aa6902aced311c85fe48c (patch)
tree07acca4e2c827a181d46286257f5deb1a6759f3f
parent5ab4bc67d818ba27388d64cb9c52cb0c3bdac254 (diff)
downloadlinux-3e924e9272c80939677aa6902aced311c85fe48c.tar.xz
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>
-rw-r--r--kernel/bpf/btf.c66
-rw-r--r--kernel/bpf/verifier.c4
2 files changed, 54 insertions, 16 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 6fb3461e3ac2..68921d9172b5 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 5d8f2656dbfd..8ed484cb1a8a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -16503,6 +16503,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;