bpf: Do not allow tail call in strcut_ops program with __ref argument
authorAmery Hung <ameryhung@gmail.com>
Thu, 20 Feb 2025 22:15:31 +0000 (14:15 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 21 Feb 2025 02:44:35 +0000 (18:44 -0800)
Reject struct_ops programs with refcounted kptr arguments (arguments
tagged with __ref suffix) that tail call. Once a refcounted kptr is
passed to a struct_ops program from the kernel, it can be freed or
xchged into maps. As there is no guarantee a callee can get the same
valid refcounted kptr in the ctx, we cannot allow such usage.

Signed-off-by: Amery Hung <ameryhung@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250220221532.1079331-1-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index fcdeb00e21c3b4c6161951439071c6404985eed0..bb086eb0c5b28889a19b30be3b6b45c3af4c2fac 100644 (file)
@@ -22542,10 +22542,11 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
        const struct bpf_struct_ops *st_ops;
        const struct btf_member *member;
        struct bpf_prog *prog = env->prog;
+       bool has_refcounted_arg = false;
        u32 btf_id, member_idx;
        struct btf *btf;
        const char *mname;
-       int err;
+       int i, err;
 
        if (!prog->gpl_compatible) {
                verbose(env, "struct ops programs must have a GPL compatible license\n");
@@ -22615,6 +22616,23 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
                return -EACCES;
        }
 
+       for (i = 0; i < st_ops_desc->arg_info[member_idx].cnt; i++) {
+               if (st_ops_desc->arg_info[member_idx].info->refcounted) {
+                       has_refcounted_arg = true;
+                       break;
+               }
+       }
+
+       /* Tail call is not allowed for programs with refcounted arguments since we
+        * cannot guarantee that valid refcounted kptrs will be passed to the callee.
+        */
+       for (i = 0; i < env->subprog_cnt; i++) {
+               if (has_refcounted_arg && env->subprog_info[i].has_tail_call) {
+                       verbose(env, "program with __ref argument cannot tail call\n");
+                       return -EINVAL;
+               }
+       }
+
        prog->aux->attach_func_proto = func_proto;
        prog->aux->attach_func_name = mname;
        env->ops = st_ops->verifier_ops;