diff --git a/src/elf32_offscn.c b/src/elf32_offscn.c index 9e757c8..1a9a3b0 100644 --- a/src/elf32_offscn.c +++ b/src/elf32_offscn.c @@ -73,14 +73,15 @@ elfw2(LIBELFBITS,offscn) (Elf *elf, ElfW2(LIBELFBITS,Off) offset) for (unsigned int i = 0; i < runp->cnt; ++i) if (runp->data[i].shdr.ELFW(e,LIBELFBITS)->sh_offset == offset) { - result = &runp->data[i]; - /* If this section is empty, the following one has the same sh_offset. We presume the caller is looking for a nonempty section, so keep looking if this one is empty. */ if (runp->data[i].shdr.ELFW(e,LIBELFBITS)->sh_size != 0 && runp->data[i].shdr.ELFW(e,LIBELFBITS)->sh_type != SHT_NOBITS) - goto out; + { + result = &runp->data[i]; + goto out; + } } runp = runp->next; diff --git a/src/elf32_updatenull.c b/src/elf32_updatenull.c index 3594e8b..74c27fd 100644 --- a/src/elf32_updatenull.c +++ b/src/elf32_updatenull.c @@ -135,7 +135,8 @@ __elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum) ehdr = __elfw2(LIBELFBITS,getehdr_wrlock) (elf); /* Set the default values. */ - if (ELFW(default_ehdr,LIBELFBITS) (elf, ehdr, shnum, change_bop) != 0) + if (ehdr == NULL + || ELFW(default_ehdr,LIBELFBITS) (elf, ehdr, shnum, change_bop) != 0) return -1; /* At least the ELF header is there. */ diff --git a/src/elf_begin.c b/src/elf_begin.c index 3ed1f8d..d22f107 100644 --- a/src/elf_begin.c +++ b/src/elf_begin.c @@ -62,7 +62,7 @@ file_read_ar (int fildes, void *map_address, off_t offset, size_t maxsize, happen on demand. */ elf->state.ar.offset = offset + SARMAG; - elf->state.ar.elf_ar_hdr.ar_rawname = elf->state.ar.raw_name; + elf->state.ar.cur_ar_hdr.ar_rawname = elf->state.ar.raw_name; } return elf; @@ -815,6 +815,50 @@ read_long_names (Elf *elf) } +/* Copy archive header from parent archive ref to member descriptor elf. */ +static int +copy_arhdr (Elf_Arhdr *dest, Elf *ref) +{ + Elf_Arhdr *hdr; + + hdr = &ref->state.ar.cur_ar_hdr; + + char *ar_name = hdr->ar_name; + char *ar_rawname = hdr->ar_rawname; + if (ar_name == NULL || ar_rawname == NULL) + { + /* ref doesn't have an Elf_Arhdr or it was marked as unusable. */ + return 0; + } + + /* Allocate copies of ar_name and ar_rawname. */ + size_t name_len = strlen (ar_name) + 1; + char *name_copy = malloc (MAX (name_len, 16)); + if (name_copy == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return -1; + } + memcpy (name_copy, ar_name, name_len); + + size_t rawname_len = strlen (ar_rawname) + 1; + char *rawname_copy = malloc (MAX (rawname_len, 17)); + if (rawname_copy == NULL) + { + free (name_copy); + __libelf_seterrno (ELF_E_NOMEM); + return -1; + } + memcpy (rawname_copy, ar_rawname, rawname_len); + + *dest = *hdr; + dest->ar_name = name_copy; + dest->ar_rawname = rawname_copy; + + return 0; +} + + /* Read the next archive header. */ int internal_function @@ -862,7 +906,7 @@ __libelf_next_arhdr_wrlock (Elf *elf) /* Copy the raw name over to a NUL terminated buffer. */ *((char *) mempcpy (elf->state.ar.raw_name, ar_hdr->ar_name, 16)) = '\0'; - elf_ar_hdr = &elf->state.ar.elf_ar_hdr; + elf_ar_hdr = &elf->state.ar.cur_ar_hdr; /* Now convert the `struct ar_hdr' into `Elf_Arhdr'. Determine whether this is a special entry. */ @@ -1055,21 +1099,42 @@ dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) member the internal pointer of the archive file descriptor is pointing to. First read the header of the next member if this has not happened already. */ - if (ref->state.ar.elf_ar_hdr.ar_name == NULL + if (ref->state.ar.cur_ar_hdr.ar_name == NULL && __libelf_next_arhdr_wrlock (ref) != 0) /* Something went wrong. Maybe there is no member left. */ return NULL; /* We have all the information we need about the next archive member. - Now create a descriptor for it. */ - result = read_file (fildes, ref->state.ar.offset + sizeof (struct ar_hdr), - ref->state.ar.elf_ar_hdr.ar_size, cmd, ref); + 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 - ref->start_offset); + size_t hdr_size = sizeof (struct ar_hdr); + size_t ar_size = (size_t) ref->state.ar.cur_ar_hdr.ar_size; + if (max_size < hdr_size || max_size - hdr_size < offset) + return NULL; + + 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); - /* 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; + + result->elf_ar_hdr = ar_hdr; + } + else + { + free (ar_hdr.ar_name); + free (ar_hdr.ar_rawname); } return result; diff --git a/src/elf_end.c b/src/elf_end.c index da8f3a2..9df2e16 100644 --- a/src/elf_end.c +++ b/src/elf_end.c @@ -116,6 +116,12 @@ elf_end (Elf *elf) rwlock_unlock (parent->lock); } + if (elf->elf_ar_hdr.ar_name != NULL) + free (elf->elf_ar_hdr.ar_name); + + if (elf->elf_ar_hdr.ar_rawname != NULL) + free (elf->elf_ar_hdr.ar_rawname); + /* This was the last activation. Free all resources. */ switch (elf->kind) { diff --git a/src/elf_getarhdr.c b/src/elf_getarhdr.c index 509f1da..dcd5718 100644 --- a/src/elf_getarhdr.c +++ b/src/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 || elf->parent->kind != ELF_K_AR) { __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/src/elf_getaroff.c b/src/elf_getaroff.c index 5c102ad..5737b35 100644 --- a/src/elf_getaroff.c +++ b/src/elf_getaroff.c @@ -43,7 +43,7 @@ elf_getaroff (Elf *elf) { /* Be gratious, the specs demand it. */ if (elf == NULL || elf->parent == NULL) - return ELF_C_NULL; + return -1; /* We can be sure the parent is an archive. */ Elf *parent = elf->parent; diff --git a/src/elf_getdata_rawchunk.c b/src/elf_getdata_rawchunk.c index 010fac9..87da912 100644 --- a/src/elf_getdata_rawchunk.c +++ b/src/elf_getdata_rawchunk.c @@ -87,7 +87,7 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) int flags = 0; Elf_Data *result = NULL; - rwlock_rdlock (elf->lock); + rwlock_wrlock (elf->lock); /* Maybe we already got this chunk? */ Elf_Data_Chunk key; @@ -95,7 +95,7 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) key.data.d.d_size = size; key.data.d.d_type = type; Elf_Data_Chunk **found - = eu_tsearch (&key, &elf->state.elf.rawchunk_tree, &chunk_compare); + = eu_tsearch_nolock (&key, &elf->state.elf.rawchunk_tree, &chunk_compare); if (found == NULL) goto nomem; @@ -136,7 +136,8 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) if (rawchunk == NULL) { nomem: - eu_tdelete (&key, &elf->state.elf.rawchunk_tree, &chunk_compare); + eu_tdelete_nolock (&key, &elf->state.elf.rawchunk_tree, + &chunk_compare); __libelf_seterrno (ELF_E_NOMEM); goto out; } @@ -147,7 +148,8 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) != size)) { /* Something went wrong. */ - eu_tdelete (&key, &elf->state.elf.rawchunk_tree, &chunk_compare); + eu_tdelete_nolock (&key, &elf->state.elf.rawchunk_tree, + &chunk_compare); free (rawchunk); __libelf_seterrno (ELF_E_READ_ERROR); goto out; @@ -217,9 +219,6 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) chunk->data.d.d_version = EV_CURRENT; chunk->offset = offset; - rwlock_unlock (elf->lock); - rwlock_wrlock (elf->lock); - *found = chunk; result = &chunk->data.d; diff --git a/src/elf_next.c b/src/elf_next.c index 6edafd2..32d91b5 100644 --- a/src/elf_next.c +++ b/src/elf_next.c @@ -56,7 +56,7 @@ elf_next (Elf *elf) /* Now advance the offset. */ parent->state.ar.offset += (sizeof (struct ar_hdr) - + ((parent->state.ar.elf_ar_hdr.ar_size + 1) + + ((parent->state.ar.cur_ar_hdr.ar_size + 1) & ~1l)); /* Get the next archive header. */ @@ -64,7 +64,7 @@ elf_next (Elf *elf) /* If necessary, mark the archive header as unusable. */ if (ret == ELF_C_NULL) - parent->state.ar.elf_ar_hdr.ar_name = NULL; + parent->state.ar.cur_ar_hdr.ar_name = NULL; rwlock_unlock (parent->lock); diff --git a/src/elf_rand.c b/src/elf_rand.c index f1850e7..c08eadc 100644 --- a/src/elf_rand.c +++ b/src/elf_rand.c @@ -53,7 +53,7 @@ elf_rand (Elf *elf, size_t offset) if (__libelf_next_arhdr_wrlock (elf) != 0) { /* Mark the archive header as unusable. */ - elf->state.ar.elf_ar_hdr.ar_name = NULL; + elf->elf_ar_hdr.ar_name = NULL; return 0; } diff --git a/src/eu-search.h b/src/eu-search.h index 67b54c1..4299e11 100644 --- a/src/eu-search.h +++ b/src/eu-search.h @@ -52,6 +52,30 @@ extern void *eu_tfind (const void *key, search_tree *tree, extern void *eu_tdelete (const void *key, search_tree *tree, int (*compare)(const void *, const void *)); +/* Search TREE for KEY and add KEY if not found. No locking is performed. */ +static inline void * +eu_tsearch_nolock (const void *key, search_tree *tree, + int (*compare)(const void *, const void *)) +{ + return tsearch (key, &tree->root, compare); +} + +/* Search TREE for KEY. No locking is performed. */ +static inline void * +eu_tfind_nolock (const void *key, search_tree *tree, + int (*compare)(const void *, const void *)) +{ + return tfind (key, &tree->root, compare); +} + +/* Delete key from TREE. No locking is performed. */ +static inline void * +eu_tdelete_nolock (const void *key, search_tree *tree, + int (*compare)(const void *, const void *)) +{ + return tdelete (key, &tree->root, compare); +} + /* Free all nodes from TREE. */ void eu_tdestroy (search_tree *tree, void (*free_node)(void *)); diff --git a/src/gelf_getnote.c b/src/gelf_getnote.c index 0f7b9d6..2cf2856 100644 --- a/src/gelf_getnote.c +++ b/src/gelf_getnote.c @@ -51,8 +51,8 @@ gelf_getnote (Elf_Data *data, size_t offset, GElf_Nhdr *result, /* It's easy to handle this type. It has the same size for 32 and 64 bit objects. */ - assert (sizeof (GElf_Nhdr) == sizeof (Elf32_Nhdr)); - assert (sizeof (GElf_Nhdr) == sizeof (Elf64_Nhdr)); + eu_static_assert (sizeof (GElf_Nhdr) == sizeof (Elf32_Nhdr)); + eu_static_assert (sizeof (GElf_Nhdr) == sizeof (Elf64_Nhdr)); rwlock_rdlock (((Elf_Data_Scn *) data)->s->elf->lock); diff --git a/src/libelfP.h b/src/libelfP.h index 66e7e4d..11ef598 100644 --- a/src/libelfP.h +++ b/src/libelfP.h @@ -306,6 +306,9 @@ struct Elf /* Reference counting for the descriptor. */ int ref_count; + /* Structure returned by 'elf_getarhdr'. */ + Elf_Arhdr elf_ar_hdr; + /* Lock to handle multithreaded programs. */ rwlock_define (,lock); @@ -394,7 +397,8 @@ struct Elf int64_t offset; /* Offset in file we are currently at. elf_next() advances this to the next member of the archive. */ - Elf_Arhdr elf_ar_hdr; /* Structure returned by 'elf_getarhdr'. */ + Elf_Arhdr cur_ar_hdr; /* Copy of current archive member's structure + returned by 'elf_getarhdr'. */ struct ar_hdr ar_hdr; /* Header read from file. */ char ar_name[16]; /* NUL terminated ar_name of elf_ar_hdr. */ char raw_name[17]; /* This is a buffer for the NUL terminated