[PATCH 2/7] elf_getarhdr: Replace per-archive Elf_Arhdr storage with per-member storage
Aaron Merey
amerey@redhat.com
Tue Jul 15 04:25:52 GMT 2025
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 <amerey@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
More information about the Elfutils-devel
mailing list