]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
elf_getdata_rawchunk.c: Fix dummy chunk insertion race condition
authorAaron Merey <amerey@redhat.com>
Mon, 28 Jul 2025 20:32:28 +0000 (16:32 -0400)
committerAaron Merey <amerey@redhat.com>
Fri, 5 Sep 2025 16:41:48 +0000 (12:41 -0400)
When elf_getdata_rawchunk aquires a new chunk for the first time, it
inserts a stack-allocated dummy chunk into a search_tree with a rdlock
held.  When the real chunk is prepared to replace the dummy chunk, the
rdlock is released and a wrlock is then held while replacing the
dummy with the real chunk.

Before the wrlock is held, other threads could incorrectly acquire the
dummy chunk as if it were a real chunk.

Fix this by holding a wrlock throughout elf_getdata_rawchunk.

Signed-off-by: Aaron Merey <amerey@redhat.com>
libelf/elf_getdata_rawchunk.c

index 010fac9063d14b992a4f71b0dd178d020a658d23..87da912aa79189ad8a04929acd9daaf7b6ad0faf 100644 (file)
@@ -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;