]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Unify dynptr handling in the verifier
authorAmery Hung <ameryhung@gmail.com>
Fri, 29 May 2026 01:49:25 +0000 (18:49 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 01:31:40 +0000 (18:31 -0700)
Simplify dynptr checking for helper and kfunc by unifying it. Remember
the initialized dynptr (i.e.,g !(arg_type |= MEM_UNINIT)) pass to a
dynptr kfunc during process_dynptr_func() so that we can easily
retrieve the information for verification later. By saving it in
meta->dynptr, there is no need to call dynptr helpers such as
dynptr_id(), dynptr_ref_obj_id() and dynptr_type() in check_func_arg().

Remove and open code the helpers in process_dynptr_func() when
saving id, ref_obj_id, and type.

Besides, since dynptr ref_obj_id information is now pass around in
meta->bpf_dynptr_desc, drop the check in helper_multiple_ref_obj_use.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/20260529014936.2811085-3-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf_verifier.h
kernel/bpf/verifier.c

index 5cbad3b64130c7641cc2d92df55c08f885550d4d..3a5c226bf1c38be6ee6814d4807aa478f0d9a486 100644 (file)
@@ -1438,6 +1438,13 @@ struct bpf_map_desc {
        int uid;
 };
 
+/* The last initialized dynptr; Populated by process_dynptr_func() */
+struct bpf_dynptr_desc {
+       enum bpf_dynptr_type type;
+       u32 id;
+       u32 ref_obj_id;
+};
+
 struct bpf_kfunc_call_arg_meta {
        /* In parameters */
        struct btf *btf;
@@ -1478,16 +1485,12 @@ struct bpf_kfunc_call_arg_meta {
        struct {
                struct btf_field *field;
        } arg_rbtree_root;
-       struct {
-               enum bpf_dynptr_type type;
-               u32 id;
-               u32 ref_obj_id;
-       } initialized_dynptr;
        struct {
                u8 spi;
                u8 frameno;
        } iter;
        struct bpf_map_desc map;
+       struct bpf_dynptr_desc dynptr;
        u64 mem_size;
 };
 
index d048f303322088d8f19af12e207f53ba07b37192..0c9792d42668b49c0ae86b02c50c916888f9d4af 100644 (file)
@@ -233,6 +233,7 @@ static void bpf_map_key_store(struct bpf_insn_aux_data *aux, u64 state)
 
 struct bpf_call_arg_meta {
        struct bpf_map_desc map;
+       struct bpf_dynptr_desc dynptr;
        bool raw_mode;
        bool pkt_access;
        u8 release_regno;
@@ -241,7 +242,6 @@ struct bpf_call_arg_meta {
        int mem_size;
        u64 msize_max_value;
        int ref_obj_id;
-       int dynptr_id;
        int func_id;
        struct btf *btf;
        u32 btf_id;
@@ -470,11 +470,6 @@ static bool is_ptr_cast_function(enum bpf_func_id func_id)
                func_id == BPF_FUNC_skc_to_tcp_request_sock;
 }
 
-static bool is_dynptr_ref_function(enum bpf_func_id func_id)
-{
-       return func_id == BPF_FUNC_dynptr_data;
-}
-
 static bool is_sync_callback_calling_kfunc(u32 btf_id);
 static bool is_async_callback_calling_kfunc(u32 btf_id);
 static bool is_callback_calling_kfunc(u32 btf_id);
@@ -542,8 +537,6 @@ static bool helper_multiple_ref_obj_use(enum bpf_func_id func_id,
                ref_obj_uses++;
        if (is_acquire_function(func_id, map))
                ref_obj_uses++;
-       if (is_dynptr_ref_function(func_id))
-               ref_obj_uses++;
 
        return ref_obj_uses > 1;
 }
@@ -7221,8 +7214,9 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno,
  * use case. The second level is tracked using the upper bit of bpf_dynptr->size
  * and checked dynamically during runtime.
  */
-static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int insn_idx,
-                              enum bpf_arg_type arg_type, int clone_ref_obj_id)
+static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+                              argno_t argno, int insn_idx, enum bpf_arg_type arg_type,
+                              int clone_ref_obj_id, struct bpf_dynptr_desc *dynptr)
 {
        int spi, err = 0;
 
@@ -7287,6 +7281,8 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
                }
 
                if (reg->type != CONST_PTR_TO_DYNPTR) {
+                       struct bpf_func_state *state = bpf_func(env, reg);
+
                        spi = dynptr_get_spi(env, reg);
                        if (spi < 0)
                                return spi;
@@ -7296,6 +7292,14 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
                         * in check_helper_call and mark_btf_func_reg_size in check_kfunc_call.
                         */
                        mark_stack_slots_scratched(env, spi, BPF_DYNPTR_NR_SLOTS);
+
+                       reg = &state->stack[spi].spilled_ptr;
+               }
+
+               if (dynptr) {
+                       dynptr->type = reg->dynptr.type;
+                       dynptr->id = reg->id;
+                       dynptr->ref_obj_id = reg->ref_obj_id;
                }
        }
        return err;
@@ -8065,72 +8069,6 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
        }
 }
 
-static struct bpf_reg_state *get_dynptr_arg_reg(struct bpf_verifier_env *env,
-                                               const struct bpf_func_proto *fn,
-                                               struct bpf_reg_state *regs)
-{
-       struct bpf_reg_state *state = NULL;
-       int i;
-
-       for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++)
-               if (arg_type_is_dynptr(fn->arg_type[i])) {
-                       if (state) {
-                               verbose(env, "verifier internal error: multiple dynptr args\n");
-                               return NULL;
-                       }
-                       state = &regs[BPF_REG_1 + i];
-               }
-
-       if (!state)
-               verbose(env, "verifier internal error: no dynptr arg found\n");
-
-       return state;
-}
-
-static int dynptr_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
-{
-       struct bpf_func_state *state = bpf_func(env, reg);
-       int spi;
-
-       if (reg->type == CONST_PTR_TO_DYNPTR)
-               return reg->id;
-       spi = dynptr_get_spi(env, reg);
-       if (spi < 0)
-               return spi;
-       return state->stack[spi].spilled_ptr.id;
-}
-
-static int dynptr_ref_obj_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
-{
-       struct bpf_func_state *state = bpf_func(env, reg);
-       int spi;
-
-       if (reg->type == CONST_PTR_TO_DYNPTR)
-               return reg->ref_obj_id;
-       spi = dynptr_get_spi(env, reg);
-       if (spi < 0)
-               return spi;
-       return state->stack[spi].spilled_ptr.ref_obj_id;
-}
-
-static enum bpf_dynptr_type dynptr_get_type(struct bpf_verifier_env *env,
-                                           struct bpf_reg_state *reg)
-{
-       struct bpf_func_state *state = bpf_func(env, reg);
-       int spi;
-
-       if (reg->type == CONST_PTR_TO_DYNPTR)
-               return reg->dynptr.type;
-
-       spi = bpf_get_spi(reg->var_off.value);
-       if (spi < 0) {
-               verbose(env, "verifier internal error: invalid spi when querying dynptr type\n");
-               return BPF_DYNPTR_TYPE_INVALID;
-       }
-
-       return state->stack[spi].spilled_ptr.dynptr.type;
-}
-
 static int check_arg_const_str(struct bpf_verifier_env *env,
                               struct bpf_reg_state *reg, argno_t argno)
 {
@@ -8488,7 +8426,8 @@ skip_type_check:
                                         true, meta);
                break;
        case ARG_PTR_TO_DYNPTR:
-               err = process_dynptr_func(env, reg, argno_from_reg(regno), insn_idx, arg_type, 0);
+               err = process_dynptr_func(env, reg, argno_from_reg(regno), insn_idx, arg_type, 0,
+                                         &meta->dynptr);
                if (err)
                        return err;
                break;
@@ -9170,7 +9109,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
                        if (ret)
                                return ret;
 
-                       ret = process_dynptr_func(env, reg, argno, -1, arg->arg_type, 0);
+                       ret = process_dynptr_func(env, reg, argno, -1, arg->arg_type, 0, NULL);
                        if (ret)
                                return ret;
                } else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
@@ -10278,52 +10217,10 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
                        }
                }
                break;
-       case BPF_FUNC_dynptr_data:
-       {
-               struct bpf_reg_state *reg;
-               int id, ref_obj_id;
-
-               reg = get_dynptr_arg_reg(env, fn, regs);
-               if (!reg)
-                       return -EFAULT;
-
-
-               if (meta.dynptr_id) {
-                       verifier_bug(env, "meta.dynptr_id already set");
-                       return -EFAULT;
-               }
-               if (meta.ref_obj_id) {
-                       verifier_bug(env, "meta.ref_obj_id already set");
-                       return -EFAULT;
-               }
-
-               id = dynptr_id(env, reg);
-               if (id < 0) {
-                       verifier_bug(env, "failed to obtain dynptr id");
-                       return id;
-               }
-
-               ref_obj_id = dynptr_ref_obj_id(env, reg);
-               if (ref_obj_id < 0) {
-                       verifier_bug(env, "failed to obtain dynptr ref_obj_id");
-                       return ref_obj_id;
-               }
-
-               meta.dynptr_id = id;
-               meta.ref_obj_id = ref_obj_id;
-
-               break;
-       }
        case BPF_FUNC_dynptr_write:
        {
-               enum bpf_dynptr_type dynptr_type;
-               struct bpf_reg_state *reg;
+               enum bpf_dynptr_type dynptr_type = meta.dynptr.type;
 
-               reg = get_dynptr_arg_reg(env, fn, regs);
-               if (!reg)
-                       return -EFAULT;
-
-               dynptr_type = dynptr_get_type(env, reg);
                if (dynptr_type == BPF_DYNPTR_TYPE_INVALID)
                        return -EFAULT;
 
@@ -10515,10 +10412,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
                return -EFAULT;
        }
 
-       if (is_dynptr_ref_function(func_id))
-               regs[BPF_REG_0].dynptr_id = meta.dynptr_id;
-
-       if (is_ptr_cast_function(func_id) || is_dynptr_ref_function(func_id)) {
+       if (is_ptr_cast_function(func_id)) {
                /* For release_reference() */
                regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
        } else if (is_acquire_function(func_id, meta.map.ptr)) {
@@ -10532,6 +10426,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
                regs[BPF_REG_0].ref_obj_id = id;
        }
 
+       if (func_id == BPF_FUNC_dynptr_data) {
+               regs[BPF_REG_0].dynptr_id = meta.dynptr.id;
+               regs[BPF_REG_0].ref_obj_id = meta.dynptr.ref_obj_id;
+       }
+
        err = do_refine_retval_range(env, regs, fn->ret_type, func_id, &meta);
        if (err)
                return err;
@@ -12187,7 +12086,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                                meta->release_regno = regno;
                        } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
                                   (dynptr_arg_type & MEM_UNINIT)) {
-                               enum bpf_dynptr_type parent_type = meta->initialized_dynptr.type;
+                               enum bpf_dynptr_type parent_type = meta->dynptr.type;
 
                                if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
                                        verifier_bug(env, "no dynptr type for parent of clone");
@@ -12195,30 +12094,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                                }
 
                                dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
-                               clone_ref_obj_id = meta->initialized_dynptr.ref_obj_id;
+                               clone_ref_obj_id = meta->dynptr.ref_obj_id;
                                if (dynptr_type_refcounted(parent_type) && !clone_ref_obj_id) {
                                        verifier_bug(env, "missing ref obj id for parent of clone");
                                        return -EFAULT;
                                }
                        }
 
-                       ret = process_dynptr_func(env, reg, argno, insn_idx,
-                                                 dynptr_arg_type, clone_ref_obj_id);
+                       ret = process_dynptr_func(env, reg, argno, insn_idx, dynptr_arg_type,
+                                                 clone_ref_obj_id, &meta->dynptr);
                        if (ret < 0)
                                return ret;
-
-                       if (!(dynptr_arg_type & MEM_UNINIT)) {
-                               int id = dynptr_id(env, reg);
-
-                               if (id < 0) {
-                                       verifier_bug(env, "failed to obtain dynptr id");
-                                       return id;
-                               }
-                               meta->initialized_dynptr.id = id;
-                               meta->initialized_dynptr.type = dynptr_get_type(env, reg);
-                               meta->initialized_dynptr.ref_obj_id = dynptr_ref_obj_id(env, reg);
-                       }
-
                        break;
                }
                case KF_ARG_PTR_TO_ITER:
@@ -12849,7 +12735,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
                }
        } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
                   meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
-               enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->initialized_dynptr.type);
+               enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->dynptr.type);
 
                mark_reg_known_zero(env, regs, BPF_REG_0);
 
@@ -12873,11 +12759,11 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
                        }
                }
 
-               if (!meta->initialized_dynptr.id) {
+               if (!meta->dynptr.id) {
                        verifier_bug(env, "no dynptr id");
                        return -EFAULT;
                }
-               regs[BPF_REG_0].dynptr_id = meta->initialized_dynptr.id;
+               regs[BPF_REG_0].dynptr_id = meta->dynptr.id;
 
                /* we don't need to set BPF_REG_0's ref obj id
                 * because packet slices are not refcounted (see
@@ -13063,7 +12949,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        if (meta.release_regno) {
                struct bpf_reg_state *reg = &regs[meta.release_regno];
 
-               if (meta.initialized_dynptr.ref_obj_id) {
+               if (meta.dynptr.ref_obj_id) {
                        err = unmark_stack_slots_dynptr(env, reg);
                } else {
                        err = release_reference(env, reg->ref_obj_id);