From: Yonghong Song <yonghong.song@linux.dev>
To: Dave Marchevsky <davemarchevsky@fb.com>, bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Martin KaFai Lau <martin.lau@kernel.org>,
Kernel Team <kernel-team@fb.com>
Subject: Re: [PATCH v1 bpf-next 3/4] btf: Descend into structs and arrays during special field search
Date: Mon, 30 Oct 2023 13:56:27 -0700 [thread overview]
Message-ID: <2d795a03-aaef-403c-ad96-75fea0699137@linux.dev> (raw)
In-Reply-To: <20231023220030.2556229-4-davemarchevsky@fb.com>
On 10/23/23 3:00 PM, Dave Marchevsky wrote:
> Structs and arrays are aggregate types which contain some inner
> type(s) - members and elements - at various offsets. Currently, when
> examining a struct or datasec for special fields, the verifier does
> not look into the inner type of the structs or arrays it contains.
> This patch adds logic to descend into struct and array types when
> searching for special fields.
This indeed a great improvement. Thanks!
>
> If we have struct x containing an array:
>
> struct x {
> int a;
> u64 b[10];
> };
>
> we can construct some struct y with no array or struct members that
> has the same types at the same offsets:
>
> struct y {
> int a;
> u64 b1;
> u64 b2;
> /* ... */
> u64 b10;
> };
>
> Similarly for a struct containing a struct:
>
> struct x {
> char a;
> struct {
> int b;
> u64 c;
> } inner;
> };
>
> there's a struct y with no aggregate members and same types/offsets:
>
> struct y {
> char a;
> int inner_b __attribute__ ((aligned (8))); /* See [0] */
> u64 inner_c __attribute__ ((aligned (8)));
> };
>
> This patch takes advantage of this equivalence to 'flatten' the
> field info found while descending into struct or array members into
> the btf_field_info result array of the original type being examined.
> The resultant btf_record of the original type being searched will
> have the correct fields at the correct offsets, but without any
> differentiation between "this field is one of my members" and "this
> field is actually in my some struct / array member".
>
> For now this descendant search logic looks for kptr fields only.
>
> Implementation notes:
> * Search starts at btf_find_field - we're either looking at a struct
> that's the type of some mapval (btf_find_struct_field), or a
> datasec representing a .bss or .data map (btf_find_datasec_var).
> Newly-added btf_find_aggregate_field is a "disambiguation helper"
> like btf_find_field, but is meant to be called from one of the
> starting points of the search - btf_find_{struct_field,
> datasec_var}.
> * btf_find_aggregate_field may itself call btf_find_struct_field,
> so there's some recursive digging possible here
>
> * Newly-added btf_flatten_array_field handles array fields by
> finding the type of their element and continuing the dig based on
> elem type.
>
> [0]: Structs have the alignment of their largest field, so the
> explicit alignment is necessary here. Luckily this patch's
> changes don't need to care about alignment / padding, since
> the BTF created during compilation is being searched, and
> it already has the correct information.
>
> Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>
> ---
> kernel/bpf/btf.c | 151 ++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 142 insertions(+), 9 deletions(-)
>
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index e999ba85c363..b982bf6fef9d 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -3496,9 +3496,41 @@ static int __struct_member_check_align(u32 off, enum btf_field_type field_type)
> return 0;
> }
>
> +/* Return number of elems and elem_type of a btf_array
> + *
> + * If the array is multi-dimensional, return elem count of
> + * equivalent single-dimensional array
> + * e.g. int x[10][10][10] has same layout as int x[1000]
> + */
> +static u32 __multi_dim_elem_type_nelems(const struct btf *btf,
> + const struct btf_type *t,
> + const struct btf_type **elem_type)
> +{
> + u32 nelems = btf_array(t)->nelems;
> +
> + if (!nelems)
> + return 0;
> +
> + *elem_type = btf_type_by_id(btf, btf_array(t)->type);
> +
> + while (btf_type_is_array(*elem_type)) {
> + if (!btf_array(*elem_type)->nelems)
> + return 0;
btf_array(*elem_type)->nelems == 0 is a very rare case.
I guess we do not need it here. If it is indeed 0,
the final nelems will be 0 any way.
> + nelems *= btf_array(*elem_type)->nelems;
> + *elem_type = btf_type_by_id(btf, btf_array(*elem_type)->type);
> + }
> + return nelems;
> +}
> +
> +static int btf_find_aggregate_field(const struct btf *btf,
> + const struct btf_type *t,
> + struct btf_field_info_search *srch,
> + int field_off, int rec);
> +
> static int btf_find_struct_field(const struct btf *btf,
> const struct btf_type *t,
> - struct btf_field_info_search *srch)
> + struct btf_field_info_search *srch,
> + int struct_field_off, int rec)
> {
> const struct btf_member *member;
> int ret, field_type;
> @@ -3522,10 +3554,24 @@ static int btf_find_struct_field(const struct btf *btf,
> * checks, all ptrs have same align.
> * btf_maybe_find_kptr will find actual kptr type
> */
> - if (__struct_member_check_align(off, BPF_KPTR_REF))
> + if (srch->field_mask & BPF_KPTR &&
> + !__struct_member_check_align(off, BPF_KPTR_REF)) {
> + ret = btf_maybe_find_kptr(btf, member_type,
> + struct_field_off + off,
> + srch);
> + if (ret < 0)
> + return ret;
> + if (ret == BTF_FIELD_FOUND)
> + continue;
> + }
> +
> + if (!(btf_type_is_array(member_type) ||
> + __btf_type_is_struct(member_type)))
> continue;
>
> - ret = btf_maybe_find_kptr(btf, member_type, off, srch);
> + ret = btf_find_aggregate_field(btf, member_type, srch,
> + struct_field_off + off,
> + rec);
> if (ret < 0)
> return ret;
> continue;
> @@ -3541,15 +3587,17 @@ static int btf_find_struct_field(const struct btf *btf,
> case BPF_LIST_NODE:
> case BPF_RB_NODE:
> case BPF_REFCOUNT:
> - ret = btf_find_struct(btf, member_type, off, sz, field_type,
> - srch);
> + ret = btf_find_struct(btf, member_type,
> + struct_field_off + off,
> + sz, field_type, srch);
> if (ret < 0)
> return ret;
> break;
> case BPF_LIST_HEAD:
> case BPF_RB_ROOT:
> ret = btf_find_graph_root(btf, t, member_type,
> - i, off, sz, srch, field_type);
> + i, struct_field_off + off, sz,
> + srch, field_type);
> if (ret < 0)
> return ret;
> break;
> @@ -3566,6 +3614,82 @@ static int btf_find_struct_field(const struct btf *btf,
> return srch->idx;
> }
>
> +static int btf_flatten_array_field(const struct btf *btf,
> + const struct btf_type *t,
> + struct btf_field_info_search *srch,
> + int array_field_off, int rec)
> +{
> + int ret, start_idx, elem_field_cnt;
> + const struct btf_type *elem_type;
> + struct btf_field_info *info;
> + u32 i, j, off, nelems;
> +
> + if (!btf_type_is_array(t))
> + return -EINVAL;
> + nelems = __multi_dim_elem_type_nelems(btf, t, &elem_type);
> + if (!nelems || !__btf_type_is_struct(elem_type))
> + return srch->idx;
> +
> + start_idx = srch->idx;
> + ret = btf_find_struct_field(btf, elem_type, srch, array_field_off + off, rec);
As kerneltest bot mentioned, 'off' is undefined. The array_field_off
should equal the first array element struct_elem_off, right?
So I think here 'off' can be replaced with 0?
> + if (ret < 0)
> + return ret;
> +
> + /* No btf_field_info's added */
> + if (srch->idx == start_idx)
> + return srch->idx;
> +
> + elem_field_cnt = srch->idx - start_idx;
> + info = __next_field_infos(srch, elem_field_cnt * (nelems - 1));
> + if (IS_ERR_OR_NULL(info))
> + return PTR_ERR(info);
What happens if nelems = 1?
> +
> + /* Array elems after the first can copy first elem's btf_field_infos
> + * and adjust offset
> + */
> + for (i = 1; i < nelems; i++) {
> + memcpy(info, &srch->infos[start_idx],
> + elem_field_cnt * sizeof(struct btf_field_info));
> + for (j = 0; j < elem_field_cnt; j++) {
> + info->off += (i * elem_type->size);
> + info++;
> + }
> + }
> + return srch->idx;
> +}
> +
> [...]
next prev parent reply other threads:[~2023-10-30 20:56 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-23 22:00 [PATCH v1 bpf-next 0/4] Descend into struct, array types when searching for fields Dave Marchevsky
2023-10-23 22:00 ` [PATCH v1 bpf-next 1/4] bpf: Fix btf_get_field_type to fail for multiple bpf_refcount fields Dave Marchevsky
2023-10-30 17:56 ` Yonghong Song
2023-11-01 21:56 ` Andrii Nakryiko
2023-10-23 22:00 ` [PATCH v1 bpf-next 2/4] bpf: Refactor btf_find_field with btf_field_info_search Dave Marchevsky
2023-10-28 14:52 ` Jiri Olsa
2023-10-30 19:31 ` Yonghong Song
2023-10-30 19:56 ` Yonghong Song
2023-11-01 21:56 ` Andrii Nakryiko
2023-10-23 22:00 ` [PATCH v1 bpf-next 3/4] btf: Descend into structs and arrays during special field search Dave Marchevsky
2023-10-26 1:24 ` kernel test robot
2023-10-30 12:56 ` Jiri Olsa
2023-10-30 20:56 ` Yonghong Song [this message]
2023-11-01 21:56 ` Andrii Nakryiko
2023-10-23 22:00 ` [PATCH v1 bpf-next 4/4] selftests/bpf: Add tests exercising aggregate type BTF " Dave Marchevsky
2023-10-23 23:32 ` kernel test robot
2023-10-30 21:10 ` Yonghong Song
2023-11-01 21:56 ` Andrii Nakryiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2d795a03-aaef-403c-ad96-75fea0699137@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davemarchevsky@fb.com \
--cc=kernel-team@fb.com \
--cc=martin.lau@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.