Hi Mark,
On Mon, Sep 8, 2025 at 6:04 PM Mark Wielaard <[email protected]> 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 <[email protected]>
> > ---
> > 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
>