]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
elf_begin.c: Use relative offset in archive size check
authorAaron Merey <amerey@redhat.com>
Mon, 8 Sep 2025 02:17:28 +0000 (22:17 -0400)
committerAaron Merey <amerey@redhat.com>
Tue, 9 Sep 2025 13:37:21 +0000 (09:37 -0400)
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.

Signed-off-by: Aaron Merey <amerey@redhat.com>
libelf/elf_begin.c

index d3ab887d34e3db4910bf41940da63bb1ba834de4..a824893f08379d9bc1b68b44a450abd86bfabe8d 100644 (file)
@@ -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;
-
   /* 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;
   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);
   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)
+  if (max_size < hdr_size || max_size - hdr_size < offset)
     return NULL;
-  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;
+
+  result = read_file (fildes, ref->state.ar.offset + sizeof (struct ar_hdr),
+                     MIN (max_size - hdr_size - offset, ar_size), cmd, ref);
 
   if (result != NULL)
     {