[to-be-committed v2] PR ld/24600: BFD: Fix use-after-free from `_bfd_load_armap'

"Maciej W. Rozycki" <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
From: Alan Modra <[email protected]>

Fix an issue in commit e34fd4bfa6d7 ("PR ld/24600: BFD: Add general 
linker support for mapless archives") where the symbol map created by 
`_bfd_load_armap' has its entries discarded by a call to `bfd_release' 
after return to `_bfd_compute_and_push_armap' where all objalloc memory
is freed that came starting from the dummy `first_name' allocation.

Restructure `_bfd_compute_and_push_armap' code such that temporary name 
pointers are kept in local objalloc and always discarded upon exit from 
the function, while actual name strings are retained according to the
`keep_symtab' parameter and copies are not made in `_bfd_load_armap' 
anymore.

This has been found in the context of CVE-2026-19548, 
<https://nvd.nist.gov/vuln/detail/CVE-2026-19548>.

Co-Authored-By: Maciej W. Rozycki <[email protected]>

	PR ld/24600
	* archive.c (_bfd_load_armap): Don't copy name.
	(_bfd_compute_and_push_armap): Use local objalloc for name 
	pointers.  Don't release bfd_alloc'd memory when keep_symdefs.
---
Hi,

 This has passed regression-testing across my usual 263 targets.

 Given the reduction of the change I've kept the function result casts in 
this revision.  A separate clean-up with no functional change can be done 
separately.

 I note that the failure of `push_armap' does not cause the symbol cache 
to be discarded in the `keep_symdefs' case even though it probably should.  
I'll offer a separate change for that.

 Again, I'm going to check this in shortly unless I hear objections.

  Maciej

Changes from v1,
<https://inbox.sourceware.org/binutils/[email protected]/>:

- Fold in Alan's changes for symbol name strings from 
  <https://inbox.sourceware.org/binutils/[email protected]/>, 
  discarding unrelated clean-up bits.

- Also release memory upon `push_armap' failure.
---
 bfd/archive.c |   48 ++++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

binutils-bfd-compute-and-push-armap-objalloc.diff
Index: binutils-gdb/bfd/archive.c
===================================================================
--- binutils-gdb.orig/bfd/archive.c
+++ binutils-gdb/bfd/archive.c
@@ -135,6 +135,7 @@ SUBSECTION
 #include "bfd.h"
 #include "libiberty.h"
 #include "libbfd.h"
+#include "objalloc.h"
 #include "aout/ar.h"
 #include "aout/ranlib.h"
 #include "safe-ctype.h"
@@ -1023,29 +1024,13 @@ _bfd_load_armap (bfd *arch, unsigned int
        counter < ardata->symdef_count;
        counter++, set++)
     {
-      bfd_size_type namelen = strlen (*map[counter].name) + 1;
-      char *name = bfd_alloc (arch, namelen);
-
-      if (name == NULL)
-	{
-	  bfd_set_error (bfd_error_no_memory);
-	  goto release_symdefs;
-	}
-
-      memcpy (name, *map[counter].name, namelen);
-      set->name = name;
+      set->name = *map[counter].name;
       set->u.abfd = map[counter].abfd;
     }
 
   ardata->symdef_use_bfd = true;
   arch->has_armap = true;
   return true;
-
- release_symdefs:
-  bfd_release (arch, ardata->symdefs);
-  ardata->symdef_count = 0;
-  ardata->symdefs = NULL;
-  return false;
 }
 
 /* Iterate over members of archive ARCH starting from FIRST_ONE and
@@ -2461,6 +2446,7 @@ _bfd_compute_and_push_armap
   (bfd *arch, unsigned int elength, bool keep_symtab,
    bool (*push_armap) (bfd *, unsigned int, struct orl *, unsigned int, int))
 {
+  struct objalloc *scratch;
   char *first_name = NULL;
   bfd *current;
   struct orl *map = NULL;
@@ -2473,17 +2459,20 @@ _bfd_compute_and_push_armap
   size_t amt;
   static bool report_plugin_err = true;
 
+  /* We put the symbol names on the arch objalloc and pointers to them
+     on our scratch objalloc, and then discard them as appropriate.  */
+  scratch = objalloc_create ();
+  if (scratch == NULL)
+    goto objalloc_error_return;
+  first_name = (char *) bfd_alloc (arch, 1);
+  if (first_name == NULL)
+    goto error_return;
+
   amt = orl_max * sizeof (struct orl);
   map = (struct orl *) bfd_malloc (amt);
   if (map == NULL)
     goto error_return;
 
-  /* We put the symbol names on the arch objalloc, and then discard
-     them when done.  */
-  first_name = (char *) bfd_alloc (arch, 1);
-  if (first_name == NULL)
-    goto error_return;
-
   /* Drop all the files called __.SYMDEF, we're going to make our own.  */
   while (arch->archive_head
 	 && strcmp (bfd_get_filename (arch->archive_head), "__.SYMDEF") == 0)
@@ -2579,9 +2568,10 @@ _bfd_compute_and_push_armap
 			}
 		      namelen = strlen (syms[src_count]->name);
 		      amt = sizeof (char *);
-		      map[orl_count].name = (char **) bfd_alloc (arch, amt);
+		      map[orl_count].name = (char **) objalloc_alloc (scratch,
+								      amt);
 		      if (map[orl_count].name == NULL)
-			goto error_return;
+			goto objalloc_error_return;
 		      *(map[orl_count].name) = (char *) bfd_alloc (arch,
 								   namelen + 1);
 		      if (*(map[orl_count].name) == NULL)
@@ -2605,19 +2595,25 @@ _bfd_compute_and_push_armap
 
   /* OK, now we have collected all the data, let's push them out.  */
   ret = push_armap (arch, elength, map, orl_count, stridx);
+  if (!ret)
+    goto error_return;
 
   free (syms);
   free (map);
-  if (first_name != NULL)
+  if (!keep_symtab)
     bfd_release (arch, first_name);
+  objalloc_free (scratch);
 
   return ret;
 
+ objalloc_error_return:
+  bfd_set_error (bfd_error_no_memory);
  error_return:
   free (syms);
   free (map);
   if (first_name != NULL)
     bfd_release (arch, first_name);
+  objalloc_free (scratch);
 
   return false;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.