Re: [PATCH bpf-next 5/6] libbpf: fix BTF-to-C converter's padding logic

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 2022-12-12 at 10:44 -0800, Andrii Nakryiko wrote:
> On Fri, Dec 9, 2022 at 9:21 AM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
> > 
> > On Thu, 2022-12-08 at 10:57 -0800, Andrii Nakryiko wrote:
> > > Turns out that btf_dump API doesn't handle a bunch of tricky corner
> > > cases, as reported by Per, and further discovered using his testing
> > > Python script ([0]).
> > > 
> > > This patch revamps btf_dump's padding logic significantly, making it
> > > more correct and also avoiding unnecessary explicit padding, where
> > > compiler would pad naturally. This overall topic turned out to be very
> > > tricky and subtle, there are lots of subtle corner cases. The comments
> > > in the code tries to give some clues, but comments themselves are
> > > supposed to be paired with good understanding of C alignment and padding
> > > rules. Plus some experimentation to figure out subtle things like
> > > whether `long :0;` means that struct is now forced to be long-aligned
> > > (no, it's not, turns out).
> > > 
> > > Anyways, Per's script, while not completely correct in some known
> > > situations, doesn't show any obvious cases where this logic breaks, so
> > > this is a nice improvement over the previous state of this logic.
> > > 
> > > Some selftests had to be adjusted to accommodate better use of natural
> > > alignment rules, eliminating some unnecessary padding, or changing it to
> > > `type: 0;` alignment markers.
> > > 
> > > Note also that for when we are in between bitfields, we emit explicit
> > > bit size, while otherwise we use `: 0`, this feels much more natural in
> > > practice.
> > > 
> > > Next patch will add few more test cases, found through randomized Per's
> > > script.
> > > 
> > >   [0] https://lore.kernel.org/bpf/85f83c333f5355c8ac026f835b18d15060725fcb.camel@xxxxxxxxxxxx/
> > > 
> > > Reported-by: Per Sundström XP <per.xp.sundstrom@xxxxxxxxxxxx>
> > > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > > ---
> > >  tools/lib/bpf/btf_dump.c                      | 169 +++++++++++++-----
> > >  .../bpf/progs/btf_dump_test_case_bitfields.c  |   2 +-
> > >  .../bpf/progs/btf_dump_test_case_padding.c    |  58 ++++--
> > >  3 files changed, 164 insertions(+), 65 deletions(-)
> > > 
> > > diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
> > > index 234e82334d56..d708452c9952 100644
> > > --- a/tools/lib/bpf/btf_dump.c
> > > +++ b/tools/lib/bpf/btf_dump.c
> > > @@ -830,6 +830,25 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
> > >       }
> > >  }
> > > 
> > > +static int btf_natural_align_of(const struct btf *btf, __u32 id)
> > > +{
> > > +     const struct btf_type *t = btf__type_by_id(btf, id);
> > > +     int i, align, vlen;
> > > +     const struct btf_member *m;
> > > +
> > > +     if (!btf_is_composite(t))
> > > +             return btf__align_of(btf, id);
> > > +
> > > +     align = 1;
> > > +     m = btf_members(t);
> > > +     vlen = btf_vlen(t);
> > > +     for (i = 0; i < vlen; i++, m++) {
> > > +             align = max(align, btf_natural_align_of(btf, m->type));
> > > +     }
> > > +
> > > +     return align;
> > > +}
> > > +
> > 
> > The btf_natural_align_of() recursively visits nested structures.
> > However, the "packed" relation is non-recursive (see entry for
> > "packed" in [1]). Such mismatch leads to the following example being
> > printed incorrectly:
> > 
> >         struct a {
> >                 int x;
> >         };
> > 
> >         struct b {
> >                 struct a a;
> >                 char c;
> >         } __attribute__((packed));
> > 
> >         struct c {
> >                 struct b b1;
> >                 short a1;
> >                 struct b b2;
> >         };
> > 
> > The bpftool output looks as follows:
> > 
> >         struct a {
> >                 int x;
> >         };
> > 
> >         struct b {
> >                 struct a a;
> >                 char c;
> >         } __attribute__((packed));
> > 
> >         struct c {
> >                 struct b b1;
> >                 short: 0;
> >                 short a1;
> >                 struct b b2;
> >         } __attribute__((packed));
> 
> Nice find, thank you! The fix is very simple:
> 
> diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
> index d708452c9952..d6fd93a57f11 100644
> --- a/tools/lib/bpf/btf_dump.c
> +++ b/tools/lib/bpf/btf_dump.c
> @@ -843,7 +843,7 @@ static int btf_natural_align_of(const struct btf
> *btf, __u32 id)
>         m = btf_members(t);
>         vlen = btf_vlen(t);
>         for (i = 0; i < vlen; i++, m++) {
> -               align = max(align, btf_natural_align_of(btf, m->type));
> +               align = max(align, btf__align_of(btf, m->type));
>         }
> 
> I'll also add your example to selftests to make sure.
> 
> [...]
> 
> > 
> > Also the following comment in [2] is interesting:
> > 
> > >  If the member is a structure, then the structure has an alignment
> > >  of 1-byte, but the members of that structure continue to have their
> > >  natural alignment.
> > 
> 
> If I read it correctly, it just means that within that nested struct
> all the members are aligned naturally (unless nested struct itself is
> packed), which makes sense and is already handled correctly. Or did I
> miss some subtle point you are making?

No additional subtle points.
It's a consequence of the non-recursiveness of "packed" and I wanted to
find a direct confirmation of such behaviour in the doc as it seemed odd.

> 
> > Which leads to unaligned access in the following case:
> > 
> >         int foo(struct a *p) { return p->x; }
> > 
> >         int main() {
> >           struct b b[2] = {{ {1}, 2 }, { {3}, 4 }};
> >           printf("%i, %i\n", foo(&b[0].a), foo(&b[1].a));
> >         }
> > 
> >         $ gcc -Wall test.c
> >         test.c: In function ‘main’:
> >         test.c:38:26: warning: taking address of packed member of ‘struct b’ may result
> >                       in an unaligned pointer value [-Waddress-of-packed-member]
> >            38 |   printf("%i, %i\n", foo(&b[0].a), foo(&b[1].a));
> > 
> > (This works fine on my x86 machine, but would be an issue on arm as
> >  far as I understand).
> > 
> > [1] https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Common-Type-Attributes.html#Common-Type-Attributes
> > [2] https://developer.arm.com/documentation/100748/0607/writing-optimized-code/packing-data-structures
> > 
> 
> [...]





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux