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, ®_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 > >

