bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andrii.nakryiko@gmail.com>
To: Delyan Kratunov <delyank@fb.com>
Cc: "daniel@iogearbox.net" <daniel@iogearbox.net>,
	"ast@kernel.org" <ast@kernel.org>,
	"andrii@kernel.org" <andrii@kernel.org>,
	"bpf@vger.kernel.org" <bpf@vger.kernel.org>
Subject: Re: [PATCH bpf-next v2 1/1] bpftool: bpf skeletons assert type sizes
Date: Mon, 14 Feb 2022 21:11:03 -0800	[thread overview]
Message-ID: <CAEf4BzYZ7r3hpUsEQvkF-fpJhHdt0OXAxJxPvPDN-f4088bM6A@mail.gmail.com> (raw)
In-Reply-To: <6c673f48d35fd06bc3490b00d4e6527b7e180d59.1644884357.git.delyank@fb.com>

On Mon, Feb 14, 2022 at 4:27 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> When emitting type declarations in skeletons, bpftool will now also emit
> static assertions on the size of the data/bss/rodata/etc fields. This
> ensures that in situations where userspace and kernel types have the same
> name but differ in size we do not silently produce incorrect results but
> instead break the build.
>
> This was reported in [1] and as expected the repro in [2] fails to build
> on the new size assert after this change.
>
>   [1]: Closes: https://github.com/libbpf/libbpf/issues/433
>   [2]: https://github.com/fuweid/iovisor-bcc-pr-3777
>
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>  tools/bpf/bpftool/gen.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index 6f2e20be0c62..e7f11899437a 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -205,6 +205,29 @@ static int codegen_datasec_def(struct bpf_object *obj,
>                 off = sec_var->offset + sec_var->size;
>         }
>         printf("        } *%s;\n", sec_ident);
> +
> +       /* Walk through the section again to emit size asserts */
> +       sec_var = btf_var_secinfos(sec);
> +       for (i = 0; i < vlen; i++, sec_var++) {
> +               const struct btf_type *var = btf__type_by_id(btf, sec_var->type);
> +               const char *var_name = btf__name_by_offset(btf, var->name_off);
> +               __u32 var_type_id = var->type;
> +               __s64 var_size = btf__resolve_size(btf, var_type_id);
> +
> +               /* static variables are not exposed through BPF skeleton */
> +               if (btf_var(var)->linkage == BTF_VAR_STATIC)
> +                       continue;
> +
> +               var_ident[0] = '\0';
> +               strncat(var_ident, var_name, sizeof(var_ident) - 1);
> +               sanitize_identifier(var_ident);
> +
> +               printf("\tBPF_STATIC_ASSERT(");
> +               printf("sizeof(((struct %s__%s*)0)->%s) == %lld, ",
> +                      obj_name, sec_ident, var_ident, var_size);
> +               printf("\"unexpected size of field %s\");\n", var_ident);
> +       }
> +

So doing it right after each section really pollutes the layout of the
skeleton's struct and hurts readability a lot.

How about adding all those _Static_asserts in <skeleton__elf_bytes()
function, after the huge binary dump, to get it out of sight? I think
if we are doing asserts, we might as well validate that not just
sizes, but also each variable's offset within the section is right.

Those huge struct casts are also pretty verbose. What if we do
something like this (assuming we are in a separate function, but we
can easily just do that in __elf_bytes(). Let's use test_skeleton as
skeleton name

struct test_skeleton *s = (void *)0;

_Static_assert(sizeof(s->data->in1) == 4, "invalid size of in1");
_Static_assert(offsetof(typeof(*skel->data), in1) == 0, "invalid
offset of in1");
...
_Static_assert(sizeof(s->data_read_mostly->read_mostly_var) == 4,
"invalid size of read_mostly_var");
_Static_assert(offsetof(typeof(*skel->data_read_mostly),
read_mostly_var) == 0, "invalid offset of read_mostly_var");

(void)s; /* avoid unused variable warning */

WDYT?

>         return 0;
>  }
>
> @@ -756,6 +779,12 @@ static int do_skeleton(int argc, char **argv)
>                                                                             \n\
>                 #include <bpf/skel_internal.h>                              \n\
>                                                                             \n\
> +               #ifdef __cplusplus                                          \n\
> +               #define BPF_STATIC_ASSERT static_assert                     \n\
> +               #else                                                       \n\
> +               #define BPF_STATIC_ASSERT _Static_assert                    \n\
> +               #endif                                                      \n\

Maybe just:

#ifdef __cplusplus
#define _Static_assert static_assert
#endif

? Or that doesn't work?

BPF_STATIC_ASSERT sounds very BPF-y, while this should stay within the skeleton.

Also any such macro has to be #undef in this file, otherwise it will
"leak" into the user's code (as this is just a header file included in
user's .c files).



> +                                                                           \n\
>                 struct %1$s {                                               \n\
>                         struct bpf_loader_ctx ctx;                          \n\
>                 ",
> @@ -774,6 +803,12 @@ static int do_skeleton(int argc, char **argv)
>                 #include <stdlib.h>                                         \n\
>                 #include <bpf/libbpf.h>                                     \n\
>                                                                             \n\
> +               #ifdef __cplusplus                                          \n\
> +               #define BPF_STATIC_ASSERT static_assert                     \n\
> +               #else                                                       \n\
> +               #define BPF_STATIC_ASSERT _Static_assert                    \n\
> +               #endif                                                      \n\
> +                                                                           \n\
>                 struct %1$s {                                               \n\
>                         struct bpf_object_skeleton *skeleton;               \n\
>                         struct bpf_object *obj;                             \n\
> --
> 2.34.1

  reply	other threads:[~2022-02-15  5:11 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-15  0:26 [PATCH bpf-next v2 0/1] Avoid size mismatches in skeletons Delyan Kratunov
2022-02-15  0:26 ` [PATCH bpf-next v2 1/1] bpftool: bpf skeletons assert type sizes Delyan Kratunov
2022-02-15  5:11   ` Andrii Nakryiko [this message]
2022-02-15 17:27     ` Delyan Kratunov
2022-02-15 17:55       ` 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=CAEf4BzYZ7r3hpUsEQvkF-fpJhHdt0OXAxJxPvPDN-f4088bM6A@mail.gmail.com \
    --to=andrii.nakryiko@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=delyank@fb.com \
    /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;
as well as URLs for NNTP newsgroup(s).