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 <[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; - /* 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) 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) { -- 2.51.0
