Currently each archive descriptor maintains a single Elf_Arhdr for the current archive member (as determined by elf_next or elf_rand) which is returned by elf_getarhdr.
A single per-archive Elf_Arhdr is not ideal since elf_next and elf_rand can invalidate an archive member's reference to its own Elf_Arhdr. Avoid this possible invalidation by storing each Elf_Arhdr in its archive member descriptor. The existing Elf_Arhdr parsing and storage in the archive descriptor internal state is left mostly untouched. When an archive member is created with elf_begin it is given its own copy of its Elf_Arhdr from the archive descriptor. Also update tests/arextract.c with verification that each Elf_Arhdr is distinct and remains valid after calls to elf_next that would have previously invalidated the Elf_Arhdr. Signed-off-by: Aaron Merey <ame...@redhat.com> --- libelf/elf_begin.c | 5 +++- libelf/elf_clone.c | 1 + libelf/elf_getarhdr.c | 22 ++------------ libelf/libelfP.h | 3 ++ tests/arextract.c | 68 ++++++++++++++++++++++++++++++++++++++----- 5 files changed, 70 insertions(+), 29 deletions(-) diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index 3ed1f8d7..5ed5aaa3 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -1065,11 +1065,14 @@ dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) result = read_file (fildes, ref->state.ar.offset + sizeof (struct ar_hdr), ref->state.ar.elf_ar_hdr.ar_size, cmd, ref); - /* Enlist this new descriptor in the list of children. */ if (result != NULL) { + /* Enlist this new descriptor in the list of children. */ result->next = ref->state.ar.children; ref->state.ar.children = result; + + /* Ensure the member descriptor has its own copy of its header info. */ + result->elf_ar_hdr = ref->state.ar.elf_ar_hdr; } return result; diff --git a/libelf/elf_clone.c b/libelf/elf_clone.c index e6fe4729..d6c8d541 100644 --- a/libelf/elf_clone.c +++ b/libelf/elf_clone.c @@ -69,6 +69,7 @@ elf_clone (Elf *elf, Elf_Cmd cmd) == offsetof (struct Elf, state.elf64.scns)); retval->state.elf.scns_last = &retval->state.elf32.scns; retval->state.elf32.scns.max = elf->state.elf32.scns.max; + retval->elf_ar_hdr = elf->elf_ar_hdr; retval->class = elf->class; } diff --git a/libelf/elf_getarhdr.c b/libelf/elf_getarhdr.c index 509f1da5..ec85fa71 100644 --- a/libelf/elf_getarhdr.c +++ b/libelf/elf_getarhdr.c @@ -44,30 +44,12 @@ elf_getarhdr (Elf *elf) if (elf == NULL) return NULL; - Elf *parent = elf->parent; - /* Calling this function is not ok for any file type but archives. */ - if (parent == NULL) + if (elf->parent == NULL) { __libelf_seterrno (ELF_E_INVALID_OP); return NULL; } - /* Make sure we have read the archive header. */ - if (parent->state.ar.elf_ar_hdr.ar_name == NULL - && __libelf_next_arhdr_wrlock (parent) != 0) - { - rwlock_wrlock (parent->lock); - int st = __libelf_next_arhdr_wrlock (parent); - rwlock_unlock (parent->lock); - - if (st != 0) - /* Something went wrong. Maybe there is no member left. */ - return NULL; - } - - /* We can be sure the parent is an archive. */ - assert (parent->kind == ELF_K_AR); - - return &parent->state.ar.elf_ar_hdr; + return &elf->elf_ar_hdr; } diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 66e7e4dd..20120ad3 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -306,6 +306,9 @@ struct Elf /* Reference counting for the descriptor. */ int ref_count; + /* Per-descriptor copy of the structure returned by 'elf_getarhdr'. */ + Elf_Arhdr elf_ar_hdr; + /* Lock to handle multithreaded programs. */ rwlock_define (,lock); diff --git a/tests/arextract.c b/tests/arextract.c index 936d7f55..7920d1c9 100644 --- a/tests/arextract.c +++ b/tests/arextract.c @@ -21,12 +21,20 @@ #include <fcntl.h> #include <gelf.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <system.h> +typedef struct hdr_node { + Elf *elf; + Elf_Arhdr *hdr; + struct hdr_node *next; +} hdr_node; + +hdr_node *hdr_list = NULL; int main (int argc, char *argv[]) @@ -80,6 +88,27 @@ main (int argc, char *argv[]) exit (1); } + /* Keep a list of subelfs and their Elf_Arhdr. This is used to + verifiy that each archive member descriptor stores its own + Elf_Ahdr as opposed to the archive descriptor storing one + Elf_Ahdr at a time for all archive members. */ + hdr_node *node = calloc (1, sizeof (hdr_node)); + if (node == NULL) + { + printf ("calloc failed: %s\n", strerror (errno)); + exit (1); + } + node->elf = subelf; + node->hdr = arhdr; + + if (hdr_list != NULL) + { + node->next = hdr_list; + hdr_list = node; + } + else + hdr_list = node; + if (strcmp (arhdr->ar_name, argv[2]) == 0) { int outfd; @@ -128,8 +157,37 @@ Failed to get base address for the archive element: %s\n", exit (1); } - /* Close the descriptors. */ - if (elf_end (subelf) != 0 || elf_end (elf) != 0) + /* Close each subelf descriptor. */ + hdr_node *cur; + while ((cur = hdr_list) != NULL) + { + /* Read arhdr names to help detect if there's a problem with the + per-member Elf_Arhdr storage. */ + if (memchr (cur->hdr->ar_name, '\0', PATH_MAX) == NULL) + { + puts ("ar_name missing null character"); + exit (1); + } + + if (memchr (cur->hdr->ar_rawname, '\0', PATH_MAX) == NULL) + { + puts ("ar_rawname missing null character"); + exit (1); + } + + if (elf_end (cur->elf) != 0) + { + printf ("Error while freeing subELF descriptor: %s\n", + elf_errmsg (-1)); + exit (1); + } + + hdr_list = cur->next; + free (cur); + } + + /* Close the archive descriptor. */ + if (elf_end (elf) != 0) { printf ("Freeing ELF descriptors failed: %s", elf_errmsg (-1)); exit (1); @@ -144,12 +202,6 @@ Failed to get base address for the archive element: %s\n", /* Get next archive element. */ cmd = elf_next (subelf); - if (elf_end (subelf) != 0) - { - printf ("error while freeing sub-ELF descriptor: %s\n", - elf_errmsg (-1)); - exit (1); - } } /* When we reach this point we haven't found the given file in the -- 2.50.0