If elf_getarhdr is called on a descriptor that refers to an archive that is itself a member of an outer archive, it may return the Elf_Arhdr of the current member of the inner archive instead of Elf_Arhdr of the inner archive itself.
This also causes a memory leak: elf_end only attempts to free Elf_Arhdr fields ar_name and ar_rawname for descriptors that are not ELF_K_AR. Fix this by adding a new field ar_ar_hdr to Elf->state.ar. The ar_ar_hdr field stores the Elf_Arhdr of an archive which is itself a member of an archive. elf_getarhdr now returns ar_ar_hdr for ELF_K_AR descriptors associated with a parent archive and elf_end will free ar_ar_hdr.ar_name and ar_ar_hdr.ar_rawname. Siged-off-by: Aaron Merey <[email protected]> --- This series addresses the following bugs: https://issues.oss-fuzz.com/issues/440209723 https://issues.oss-fuzz.com/issues/440177309 There is at least one other bug in eu-readelf:process_elf where the code does not account for archives within archives: off_t aroff = elf_getaroff (elf); pure_elf = dwelf_elf_begin (fd); if (aroff > 0) { /* Archive member. */ (void) elf_rand (pure_elf, aroff); Elf *armem = elf_begin (-1, ELF_C_READ_MMAP, pure_elf); elf_end (pure_elf); pure_elf = armem; } if (pure_elf == NULL) { error (0, 0, _("cannot read ELF: %s"), elf_errmsg (-1)); return; } process_elf attempts to re-read archive members to avoid printing relocations that may have been applied earlier. But for members of an inner archive this fails. elf_rand is called with the outer archive descriptor (pure_elf) but aroff is relative to the inner archive. I will add tests for archives-within-archives cases and check other eu-* tools that may be affected. libelf/elf_begin.c | 6 +++++- libelf/elf_end.c | 8 ++++++++ libelf/elf_getarhdr.c | 5 ++++- libelf/libelfP.h | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index d3ab887d..823a4324 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -1128,8 +1128,12 @@ dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) { /* Enlist this new descriptor in the list of children. */ result->next = ref->state.ar.children; - result->state.elf.elf_ar_hdr = ar_hdr; ref->state.ar.children = result; + + if (result->kind == ELF_K_AR) + result->state.ar.ar_ar_hdr = ar_hdr; + else + result->state.elf.elf_ar_hdr = ar_hdr; } else { diff --git a/libelf/elf_end.c b/libelf/elf_end.c index 1d366127..460b09b7 100644 --- a/libelf/elf_end.c +++ b/libelf/elf_end.c @@ -124,6 +124,14 @@ elf_end (Elf *elf) if (elf->state.elf.elf_ar_hdr.ar_rawname != NULL) free (elf->state.elf.elf_ar_hdr.ar_rawname); } + else + { + if (elf->state.ar.ar_ar_hdr.ar_name != NULL) + free (elf->state.ar.ar_ar_hdr.ar_name); + + if (elf->state.ar.ar_ar_hdr.ar_rawname != NULL) + free (elf->state.ar.ar_ar_hdr.ar_rawname); + } /* This was the last activation. Free all resources. */ switch (elf->kind) diff --git a/libelf/elf_getarhdr.c b/libelf/elf_getarhdr.c index 9211fc2e..a5fde49b 100644 --- a/libelf/elf_getarhdr.c +++ b/libelf/elf_getarhdr.c @@ -51,5 +51,8 @@ elf_getarhdr (Elf *elf) return NULL; } - return &elf->state.elf.elf_ar_hdr; + if (elf->kind == ELF_K_AR) + return &elf->state.ar.ar_ar_hdr; + else + return &elf->state.elf.elf_ar_hdr; } diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 1b93da88..568d4b26 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -403,6 +403,8 @@ struct Elf char ar_name[16]; /* NUL terminated ar_name of elf_ar_hdr. */ char raw_name[17]; /* This is a buffer for the NUL terminated named raw_name used in the elf_ar_hdr. */ + Elf_Arhdr ar_ar_hdr; /* Archive header of this archive. Used when + an archive is a member of an archive. */ } ar; } state; -- 2.51.0
