Hi Mark,

On Mon, Sep 8, 2025 at 6:04 PM Mark Wielaard <m...@klomp.org> wrote:
>
> Hi Aaron,
>
> On Sun, Sep 07, 2025 at 10:17:28PM -0400, Aaron Merey wrote:
> > Before creating an elf descriptor for an archive member, dup_elf
> > verifies that the size of an archive is large enough to contain the
> > member.  This check uses the member's offset relative to the map_address
> > of the top-level archive containing the member.
> >
> > This check can incorrectly fail when an archive contains another
> > archive as a member.  For members of the inner archive, their offset
> > relative to the outer archive might be significantly larger than
> > the max_size of the inner archive.  This appears as if the offset and
> > max_size values are inconsistent and the creation of the member's elf
> > descriptor is stopped unnecessarily.
> >
> > Fix this by accounting for the inner archive's non-zero start_offset
> > when judging whether the member can fit within it.
> >
> > Also perform this size check before creating a copy of the member's
> > Elf_Arhdr to prevent leaking the header's ar_name and ar_rawname if
> > the size check fails.
>
> This all makes sense, if you know that ar.offset is the offset against
> the map_address instead of the start_offset of the Elf. And that
> maximum_size does take the start_offset into account. Which at least
> to me wasn't entirely intuitive.
>
> > Signed-off-by: Aaron Merey <ame...@redhat.com>
> > ---
> >  libelf/elf_begin.c | 20 +++++++++++---------
> >  1 file changed, 11 insertions(+), 9 deletions(-)
> >
> > diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c
> > index 823a4324..037a4234 100644
> > --- a/libelf/elf_begin.c
> > +++ b/libelf/elf_begin.c
> > @@ -1107,22 +1107,24 @@ dup_elf (int fildes, Elf_Cmd cmd, Elf *ref)
> >      /* Something went wrong.  Maybe there is no member left.  */
> >      return NULL;
> >
> > -  Elf_Arhdr ar_hdr = {0};
> > -  if (copy_arhdr (&ar_hdr, ref) != 0)
> > -    /* Out of memory.  */
> > -    return NULL;
> > -
>
> OK, move copy later, when we know it is necessary.
>
> >    /* We have all the information we need about the next archive member.
> >       Now create a descriptor for it. Check parent size can contain member. 
> >  */
> > +  if (ref->state.ar.offset < ref->start_offset)
> > +    return NULL;
>
> Yes, that would not make sense if true.
>
> >    size_t max_size = ref->maximum_size;
> > -  size_t offset = (size_t) ref->state.ar.offset;
> > +  size_t offset = (size_t) (ref->state.ar.offset - ref->start_offset);
>
> OK, take start_offset into account.
>
> >    size_t hdr_size = sizeof (struct ar_hdr);
> >    size_t ar_size = (size_t) ref->state.ar.elf_ar_hdr.ar_size;
> >    if (max_size - hdr_size < offset)
> >      return NULL;
>
> Note that this assumes there really is at least an header.
> Which I think is an correct assumption if we end up here.
> But maybe a more conservative check would be:
>
>     if (max_size < hdr_size || max_size - hdr_size < offset)
>       return NULL;
>
> What do you think, too paranoid?

We might as well check this too. I'll add this and merge the patch.

Aaron

>
> > -  else
> > -    result = read_file (fildes, ref->state.ar.offset + sizeof (struct 
> > ar_hdr),
> > -                     MIN (max_size - hdr_size - offset, ar_size), cmd, 
> > ref);
> > +
> > +  Elf_Arhdr ar_hdr = {0};
> > +  if (copy_arhdr (&ar_hdr, ref) != 0)
> > +    /* Out of memory.  */
> > +    return NULL;
>
> OK, swap these calls.
>
> > +  result = read_file (fildes, ref->state.ar.offset + sizeof (struct 
> > ar_hdr),
> > +                   MIN (max_size - hdr_size - offset, ar_size), cmd, ref);
>
> Right, here we use ar.offset instead of offset (plus the hdr_size) to
> set the new Elf member start_offset, which is correct. The MIN
> calculation is also now correct (using offset).
>
> >    if (result != NULL)
> >      {
>
> Thanks,
>
> Mark
>

Reply via email to