On Wed, Feb 21, 2024 at 2:22 AM Kui-Feng Lee <sinquersw@xxxxxxxxx> wrote: > > > > On 2/19/24 19:51, Menglong Dong wrote: > > In this commit, we add the 'accessed_args' field to struct bpf_prog_aux, > > which is used to record the accessed index of the function args in > > btf_ctx_access(). > > > > Meanwhile, we add the function btf_check_func_part_match() to compare the > > accessed function args of two function prototype. This function will be > > used in the following commit. > > > > Signed-off-by: Menglong Dong <dongmenglong.8@xxxxxxxxxxxxx> > > --- > > include/linux/bpf.h | 4 ++ > > kernel/bpf/btf.c | 121 ++++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 125 insertions(+) > > > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > > index c7aa99b44dbd..0225b8dbdd9d 100644 > > --- a/include/linux/bpf.h > > +++ b/include/linux/bpf.h > > @@ -1464,6 +1464,7 @@ struct bpf_prog_aux { > > const struct btf_type *attach_func_proto; > > /* function name for valid attach_btf_id */ > > const char *attach_func_name; > > + u64 accessed_args; > > struct bpf_prog **func; > > void *jit_data; /* JIT specific data. arch dependent */ > > struct bpf_jit_poke_descriptor *poke_tab; > > @@ -2566,6 +2567,9 @@ struct bpf_reg_state; > > int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog); > > int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *prog, > > struct btf *btf, const struct btf_type *t); > > +int btf_check_func_part_match(struct btf *btf1, const struct btf_type *t1, > > + struct btf *btf2, const struct btf_type *t2, > > + u64 func_args); > > const char *btf_find_decl_tag_value(const struct btf *btf, const struct btf_type *pt, > > int comp_idx, const char *tag_key); > > int btf_find_next_decl_tag(const struct btf *btf, const struct btf_type *pt, > > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c > > index 6ff0bd1a91d5..3a6931402fe4 100644 > > --- a/kernel/bpf/btf.c > > +++ b/kernel/bpf/btf.c > > @@ -6203,6 +6203,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, > > /* skip first 'void *__data' argument in btf_trace_##name typedef */ > > args++; > > nr_args--; > > + prog->aux->accessed_args |= (1 << (arg + 1)); > > + } else { > > + prog->aux->accessed_args |= (1 << arg); > > } > > > > if (arg > nr_args) { > > @@ -7010,6 +7013,124 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr > > return btf_check_func_type_match(log, btf1, t1, btf2, t2); > > } > > > > +static u32 get_ctx_arg_total_size(struct btf *btf, const struct btf_type *t) > > +{ > > + const struct btf_param *args; > > + u32 size = 0, nr_args; > > + int i; > > + > > + nr_args = btf_type_vlen(t); > > + args = (const struct btf_param *)(t + 1); > > + for (i = 0; i < nr_args; i++) { > > + t = btf_type_skip_modifiers(btf, args[i].type, NULL); > > + size += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8); > > + } > > + > > + return size; > > +} > > + > > +static int get_ctx_arg_idx_aligned(struct btf *btf, const struct btf_type *t, > > + int off) > > +{ > > + const struct btf_param *args; > > + u32 offset = 0, nr_args; > > + int i; > > + > > + nr_args = btf_type_vlen(t); > > + args = (const struct btf_param *)(t + 1); > > + for (i = 0; i < nr_args; i++) { > > + if (offset == off) > > + return i; > > + > > + t = btf_type_skip_modifiers(btf, args[i].type, NULL); > > + offset += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8); > > + if (offset > off) > > + return -1; > > + } > > + return -1; > > +} > > This one is very similar to get_ctx_arg_idx(). > How about to refactor get_ctx_arg_idx() and share the code > between get_ctx_arg_idx() and get_ctx_arg_idx_aligned(). > This seems to work, I'll combine them in the next version. Thanks for the example code! Menglong Dong > For example, > > -static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type > *func_proto, > - int off) > +static u32 _get_ctx_arg_idx(struct btf *btf, const struct btf_type > *func_proto, > + int off, u32 *arg_off) > { > const struct btf_param *args; > const struct btf_type *t; > u32 offset = 0, nr_args; > int i; > > if (!func_proto) > return off / 8; > > nr_args = btf_type_vlen(func_proto); > args = (const struct btf_param *)(func_proto + 1); > for (i = 0; i < nr_args; i++) { > + if (arg_off) > + *arg_off = offset; > t = btf_type_skip_modifiers(btf, args[i].type, NULL); > offset += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8); > if (off < offset) > return i; > } > > + if (arg_off) > + *arg_off = offset; > t = btf_type_skip_modifiers(btf, func_proto->type, NULL); > offset += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8); > if (off < offset) > return nr_args; > > return nr_args + 1; > } > > +static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type > *func_proto, > + int off) > +{ > + return _get_ctx_arg_idx(btf, func_proto, off, NULL); > +} > + > +static u32 get_ctx_arg_idx_aligned(struct btf *btf, > + const struct btf_type *func_proto, > + int off) > +{ > + u32 arg_off; > + u32 arg_idx = _get_ctx_arg_idx(btf, func_proto, off, &arg_off); > + return arg_off == off ? arg_idx : -1; > +} > + > > > + > > +/* This function is similar to btf_check_func_type_match(), except that it > > + * only compare some function args of the function prototype t1 and t2. > > + */ > > +int btf_check_func_part_match(struct btf *btf1, const struct btf_type *func1, > > + struct btf *btf2, const struct btf_type *func2, > > + u64 func_args) > > +{ > > + const struct btf_param *args1, *args2; > > + u32 nargs1, i, offset = 0; > > + const char *s1, *s2; > > + > > + if (!btf_type_is_func_proto(func1) || !btf_type_is_func_proto(func2)) > > + return -EINVAL; > > + > > + args1 = (const struct btf_param *)(func1 + 1); > > + args2 = (const struct btf_param *)(func2 + 1); > > + nargs1 = btf_type_vlen(func1); > > + > > + for (i = 0; i <= nargs1; i++) { > > + const struct btf_type *t1, *t2; > > + > > + if (!(func_args & (1 << i))) > > + goto next; > > + > > + if (i < nargs1) { > > + int t2_index; > > + > > + /* get the index of the arg corresponding to args1[i] > > + * by the offset. > > + */ > > + t2_index = get_ctx_arg_idx_aligned(btf2, func2, > > + offset); > > + if (t2_index < 0) > > + return -EINVAL; > > + > > + t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL); > > + t2 = btf_type_skip_modifiers(btf2, args2[t2_index].type, > > + NULL); > > + } else { > > + /* i == nargs1, this is the index of return value of t1 */ > > + if (get_ctx_arg_total_size(btf1, func1) != > > + get_ctx_arg_total_size(btf2, func2)) > > + return -EINVAL; > > + > > + /* check the return type of t1 and t2 */ > > + t1 = btf_type_skip_modifiers(btf1, func1->type, NULL); > > + t2 = btf_type_skip_modifiers(btf2, func2->type, NULL); > > + } > > + > > + if (t1->info != t2->info || > > + (btf_type_has_size(t1) && t1->size != t2->size)) > > + return -EINVAL; > > + if (btf_type_is_int(t1) || btf_is_any_enum(t1)) > > + goto next; > > + > > + if (btf_type_is_struct(t1)) > > + goto on_struct; > > + > > + if (!btf_type_is_ptr(t1)) > > + return -EINVAL; > > + > > + t1 = btf_type_skip_modifiers(btf1, t1->type, NULL); > > + t2 = btf_type_skip_modifiers(btf2, t2->type, NULL); > > + if (!btf_type_is_struct(t1) || !btf_type_is_struct(t2)) > > + return -EINVAL; > > + > > +on_struct: > > + s1 = btf_name_by_offset(btf1, t1->name_off); > > + s2 = btf_name_by_offset(btf2, t2->name_off); > > + if (strcmp(s1, s2)) > > + return -EINVAL; > > +next: > > + if (i < nargs1) { > > + t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL); > > + offset += btf_type_is_ptr(t1) ? 8 : roundup(t1->size, 8); > > + } > > + } > > + > > + return 0; > > +} > > + > > static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t) > > { > > const char *name;