Re: GCC-BPF triggers double free in libbpf Error: failed to link 'linked_maps2.bpf.o': Cannot allocate memory (12)

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

 



On Thu, 2023-03-23 at 09:36 -0700, Andrii Nakryiko wrote:
> On Thu, Mar 23, 2023 at 5:57 AM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
> > 
> > On Wed, 2023-03-22 at 22:18 -0700, Andrii Nakryiko wrote:
> > > On Wed, Mar 22, 2023 at 9:26 PM James Hilliard
> > > <james.hilliard1@xxxxxxxxx> wrote:
> > > > 
> > > > I'm seeing this gen object error with gcc does not occur in llvm for a
> > > > bpf test(which uses both linked_maps1.c and linked_maps2.c) in
> > > > bpf-next.
> > > > 
> > > > I presume there is a bug in GCC which is triggering a double free bug in libbpf.
> > > > 
> > > 
> > > Could you somehow share .o files for linked_maps1.c and
> > > linked_maps2.c, so I can try to repro locally?
> > 
> > Looking at the stack trace, second free is reported here:
> > 
> >   void bpf_linker__free(struct bpf_linker *linker)
> >   {
> >         ...
> >         for (i = 1; i < linker->sec_cnt; i++) {
> >                 ...
> >                 free(sec->sec_name);
> > here -->        free(sec->raw_data);
> >                 free(sec->sec_vars);
> >                 ...
> >         }
> >         ...
> >   }
> > 
> > And first free is reported here:
> > 
> >   static int extend_sec(struct bpf_linker *linker, struct dst_sec *dst, struct src_sec *src)
> >   {
> >         ...
> >         dst_align = dst->shdr->sh_addralign;
> >         src_align = src->shdr->sh_addralign;
> >         ...
> > 
> >         dst_align_sz = (dst->sec_sz + dst_align - 1) / dst_align * dst_align;
> > 
> >         /* no need to re-align final size */
> >         dst_final_sz = dst_align_sz + src->shdr->sh_size;
> > 
> >         if (src->shdr->sh_type != SHT_NOBITS) {
> > here -->        tmp = realloc(dst->raw_data, dst_final_sz);
> >                 if (!tmp)
> >                         return -ENOMEM;
> >                 dst->raw_data = tmp;
> >                 ...
> >         }
> >         ...
> >   }
> > 
> > The documentation says that `realloc(ptr, 0)` frees `ptr`.
> > So, I assume that issue is caused by handling of empty sections.
> 
> yep, thanks for repro steps! It's a quite interesting behavior. There
> are two reallocs involved:
> 
> First, dst->raw_data is NULL, dst_final_sz is 0, realloc succeeds and
> returns non-NULL pointer (which according to documentation can be
> freed with free()). All good.
> 
> Second one, for second file, we have non-NULL dst->raw_data returned
> from previous realloc(), we pass it to realloc() with dst_final_sz
> still 0. But *NOW* we get NULL as a return (and original special
> pointer "helpfully" freed for us). This we handle as -ENOMEM and exit.
> 
> Amazingly non-error-prone behavior, of course.
> 
> > This is easy to test using object files produced by LLVM:
> > 
> >   $ touch empty
> >   $ llvm-objcopy --add-section .foobar=empty linked_maps1.bpf.o
> >   $ llvm-objcopy --add-section .foobar=empty linked_maps2.bpf.o
> >   $ bpftool --debug gen object linked_maps.linked1.o linked_maps1.bpf.o linked_maps2.bpf.o
> >   libbpf: linker: adding object file 'linked_maps1.bpf.o'...
> >   libbpf: linker: adding object file 'linked_maps2.bpf.o'...
> >   Error: failed to link 'linked_maps2.bpf.o': Cannot allocate memory (12)
> >   free(): double free detected in tcache 2
> >   Aborted (core dumped)
> > 
> > The valgrind output also matches the one attached to the original email.
> > Something like below fixes it:
> > 
> > diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
> > index d7069780984a..ff3833e55c55 100644
> > --- a/tools/lib/bpf/linker.c
> > +++ b/tools/lib/bpf/linker.c
> > @@ -1113,7 +1113,7 @@ static int extend_sec(struct bpf_linker *linker, struct dst_sec *dst, struct src
> >         /* no need to re-align final size */
> >         dst_final_sz = dst_align_sz + src->shdr->sh_size;
> > 
> > -       if (src->shdr->sh_type != SHT_NOBITS) {
> > +       if (dst_final_sz != 0 && src->shdr->sh_type != SHT_NOBITS) {
> >                 tmp = realloc(dst->raw_data, dst_final_sz);
> >                 if (!tmp)
> 
> let's maybe document this quirk instead of preventing realloc() call:
> 
> /* comment here explaining the quirks of realloc() API and it's
> inconsistent runtime behavior */
> if (!tmp && dst_final_sz > 0)
>   return -ENOMEM;
> 
> Eduard, are you going to send a proper patch for this? Thanks!

I finally got back to this and have a question which I should have
asked on Thursday, sorry. Why do you want to preserve a call to
realloc() when dst_final_sz is 0?

Suppose we have N files all merging and having an empty section,
then dst->raw_data would constantly flip-flop between 2 states:
1. special pointer to memory of size zero;
2. NULL.

This shouldn't cause any issues but is kinda weird. If realloc() is
avoided the dst->raw_data would preserve it's original state (set to
zero by add_dst_sec()).

> 
> >                         return -ENOMEM;
> > 
> > 
> > BPF selftests are passing for me with this change,
> > objcopy-based reproducer no longer reports error.
> > WDYT?
> > 
> > James, could you please test this patch with bpf-gcc?
> > (you will have to re-compile libbpf and bpftool,
> >  I had to separately do `make -C tools/bpf/bpftool`
> >  before re-building selftests for some reason)
> > 
> > Thanks,
> > Eduard
> > 
> > > 
> > > > GCC gen object failure:
> > > > 
> > > > ==2125110== Command:
> > > > /home/buildroot/bpf-next/tools/testing/selftests/bpf/tools/sbin/bpftool
> > > > --debug gen object
> > > > /home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps.linked1.o
> > > > /home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps1.bpf.o
> > > > /home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps2.bpf.o
> > > > ==2125110==
> > > > libbpf: linker: adding object file
> > > > '/home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps1.bpf.o'...
> > > > libbpf: linker: adding object file
> > > > '/home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps2.bpf.o'...
> > > > Error: failed to link
> > > > '/home/buildroot/bpf-next/tools/testing/selftests/bpf/bpf_gcc/linked_maps2.bpf.o':
> > > > Cannot allocate memory (12)
> > > > ==2125110== Invalid free() / delete / delete[] / realloc()
> > > > ==2125110==    at 0x484B0C4: free (vg_replace_malloc.c:884)
> > > > ==2125110==    by 0x17F8AB: bpf_linker__free (linker.c:204)
> > > > ==2125110==    by 0x12833C: do_object (gen.c:1608)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x129B53: do_gen (gen.c:2332)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x12DB9E: main (main.c:539)
> > > > ==2125110==  Address 0xda4b420 is 0 bytes after a block of size 0 free'd
> > > > ==2125110==    at 0x484B027: free (vg_replace_malloc.c:883)
> > > > ==2125110==    by 0x484D6F8: realloc (vg_replace_malloc.c:1451)
> > > > ==2125110==    by 0x181FA3: extend_sec (linker.c:1117)
> > > > ==2125110==    by 0x182326: linker_append_sec_data (linker.c:1201)
> > > > ==2125110==    by 0x1803DC: bpf_linker__add_file (linker.c:453)
> > > > ==2125110==    by 0x12829E: do_object (gen.c:1593)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x129B53: do_gen (gen.c:2332)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x12DB9E: main (main.c:539)
> > > > ==2125110==  Block was alloc'd at
> > > > ==2125110==    at 0x484876A: malloc (vg_replace_malloc.c:392)
> > > > ==2125110==    by 0x484D6EB: realloc (vg_replace_malloc.c:1451)
> > > > ==2125110==    by 0x181FA3: extend_sec (linker.c:1117)
> > > > ==2125110==    by 0x182326: linker_append_sec_data (linker.c:1201)
> > > > ==2125110==    by 0x1803DC: bpf_linker__add_file (linker.c:453)
> > > > ==2125110==    by 0x12829E: do_object (gen.c:1593)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x129B53: do_gen (gen.c:2332)
> > > > ==2125110==    by 0x12CDAB: cmd_select (main.c:206)
> > > > ==2125110==    by 0x12DB9E: main (main.c:539)
> > 





[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