On Tue, Jun 30, 2026 at 1:45 AM Yiyang Chen
<[email protected]> wrote:
>
> BTF struct walks relax the struct-size check for accesses through a
> trailing flexible array. That is valid for ordinary BTF type walking, but
> PTR_TO_BTF_ID | MEM_ALLOC values point to objects allocated with the static
> BTF type size.
>
> When walking a MEM_ALLOC object, reject the access before applying the
> flexible-array relaxation if the access range extends past the struct size.
> Apply the same policy to struct ID matching so kfunc and kptr type checks
> do not walk past the allocated object bounds either.
>
> Fixes: 958cf2e273f0 ("bpf: Introduce bpf_obj_new")
> Fixes: 36d8bdf75a93 ("bpf: Add alloc/xchg/direct_access support for local 
> percpu kptr")
> Signed-off-by: Yiyang Chen <[email protected]>

Reviewed-by: Amery Hung <[email protected]>

> ---
>  include/linux/bpf.h   |  2 +-
>  kernel/bpf/btf.c      | 17 +++++++++++------
>  kernel/bpf/verifier.c | 11 +++++++----
>  3 files changed, 19 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 7719f6528..314d606dc 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -3146,7 +3146,7 @@ int btf_struct_access(struct bpf_verifier_log *log,
>  bool btf_struct_ids_match(struct bpf_verifier_log *log,
>                           const struct btf *btf, u32 id, int off,
>                           const struct btf *need_btf, u32 need_type_id,
> -                         bool strict);
> +                         bool strict, bool walk_flex_arrays);
>
>  int btf_distill_func_proto(struct bpf_verifier_log *log,
>                            struct btf *btf,
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 64572f85e..dff5c0d91 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -7108,7 +7108,7 @@ enum bpf_struct_walk_result {
>  static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf 
> *btf,
>                            const struct btf_type *t, int off, int size,
>                            u32 *next_btf_id, enum bpf_type_flag *flag,
> -                          const char **field_name)
> +                          const char **field_name, bool walk_flex_arrays)
>  {
>         u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
>         const struct btf_type *mtype, *elem_type = NULL;
> @@ -7135,11 +7135,14 @@ static int btf_struct_walk(struct bpf_verifier_log 
> *log, const struct btf *btf,
>                 *flag |= PTR_UNTRUSTED;
>
>         if (off + size > t->size) {
> +               struct btf_array *array_elem;
> +
> +               if (!walk_flex_arrays)
> +                       goto error;
> +
>                 /* If the last element is a variable size array, we may
>                  * need to relax the rule.
>                  */
> -               struct btf_array *array_elem;
> -
>                 if (vlen == 0)
>                         goto error;
>
> @@ -7404,7 +7407,8 @@ int btf_struct_access(struct bpf_verifier_log *log,
>
>         t = btf_type_by_id(btf, id);
>         do {
> -               err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, 
> field_name);
> +               err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag,
> +                                     field_name, !type_is_alloc(reg->type));
>
>                 switch (err) {
>                 case WALK_PTR:
> @@ -7463,7 +7467,7 @@ bool btf_types_are_same(const struct btf *btf1, u32 id1,
>  bool btf_struct_ids_match(struct bpf_verifier_log *log,
>                           const struct btf *btf, u32 id, int off,
>                           const struct btf *need_btf, u32 need_type_id,
> -                         bool strict)
> +                         bool strict, bool walk_flex_arrays)
>  {
>         const struct btf_type *type;
>         enum bpf_type_flag flag = 0;
> @@ -7482,7 +7486,8 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
>         type = btf_type_by_id(btf, id);
>         if (!type)
>                 return false;
> -       err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
> +       err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL,
> +                             walk_flex_arrays);
>         if (err != WALK_STRUCT)
>                 return false;
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 25aea4271..4d3bb7156 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -4379,7 +4379,8 @@ static int map_kptr_match_type(struct bpf_verifier_env 
> *env,
>          */
>         if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 
> reg->var_off.value,
>                                   kptr_field->kptr.btf, 
> kptr_field->kptr.btf_id,
> -                                 kptr_field->type != BPF_KPTR_UNREF))
> +                                 kptr_field->type != BPF_KPTR_UNREF,
> +                                 !type_is_alloc(reg->type)))
>                 goto bad_type;
>         return 0;
>  bad_type:
> @@ -7970,7 +7971,7 @@ static int check_reg_type(struct bpf_verifier_env *env, 
> struct bpf_reg_state *re
>
>                         if (!btf_struct_ids_match(&env->log, reg->btf, 
> reg->btf_id,
>                                                   reg->var_off.value, 
> btf_vmlinux, *arg_btf_id,
> -                                                 strict_type_match)) {
> +                                                 strict_type_match, 
> !type_is_alloc(reg->type))) {
>                                 verbose(env, "%s is of type %s but %s is 
> expected\n",
>                                         reg_arg_name(env, argno),
>                                         btf_type_name(reg->btf, reg->btf_id),
> @@ -11429,7 +11430,8 @@ static int process_kf_arg_ptr_to_btf_id(struct 
> bpf_verifier_env *env,
>         reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, &reg_ref_id);
>         reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off);
>         struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, 
> reg->var_off.value,
> -                                          meta->btf, ref_id, 
> strict_type_match);
> +                                          meta->btf, ref_id, 
> strict_type_match,
> +                                          !type_is_alloc(reg->type));
>         /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot
>          * actually use it -- it must cast to the underlying type. So we allow
>          * caller to pass in the underlying type.
> @@ -11876,7 +11878,8 @@ __process_kf_arg_ptr_to_graph_node(struct 
> bpf_verifier_env *env,
>         et = btf_type_by_id(field->graph_root.btf, 
> field->graph_root.value_btf_id);
>         t = btf_type_by_id(reg->btf, reg->btf_id);
>         if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 0, 
> field->graph_root.btf,
> -                                 field->graph_root.value_btf_id, true)) {
> +                                 field->graph_root.value_btf_id, true,
> +                                 !type_is_alloc(reg->type))) {
>                 verbose(env, "operation on %s expects arg#1 %s at offset=%d "
>                         "in struct %s, but arg is at offset=%d in struct 
> %s\n",
>                         btf_field_type_name(head_field_type),
> --
> 2.34.1
>
>

Reply via email to