public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
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;
> +}
> +
> [...]

  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox