diff mbox series

binutils: Fix CVE-2025-1148

Message ID 20250319093535.3368863-1-Harish.Sadineni@windriver.com
State New
Headers show
Series binutils: Fix CVE-2025-1148 | expand

Commit Message

Sadineni, Harish March 19, 2025, 9:35 a.m. UTC
From: Harish Sadineni <Harish.Sadineni@windriver.com>

A few place dealing with ld script handling made some attempt to free
memory, but this was generally ignored and would be quite a lot of
work to implement.  Instead, use the stat_obstack rather than
mallocing in many more cases.

Backport a patch from upstream to fix CVE-2025-1148
Upstream-Status: Backport [https://sourceware.org/cgit/binutils-gdb/commit/?id=d4115c2c8d447e297ae353892de89192c1996211]

Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>
---
 .../binutils/binutils-2.44.inc                |   1 +
 .../binutils/0016-CVE-2025-1148.patch         | 596 ++++++++++++++++++
 2 files changed, 597 insertions(+)
 create mode 100644 meta/recipes-devtools/binutils/binutils/0016-CVE-2025-1148.patch

Comments

Sadineni, Harish March 19, 2025, 9:41 a.m. UTC | #1
In upstream this patch has only been applied to the master branch and hasn't been included in any other branches yet. We've sent the backported patch to the upstream binutils-2_44 branch, but they seem hesitant to merge it into the stable branches.

This is the response I received from the original author( https://sourceware.org/pipermail/binutils/2025-March/139987.html ):
"I deliberately left that patch off the branch, and even now after it has had some time for potential problems to show up I don't think it
warrants backporting.  Memory leaks of this nature hardly qualify as bugs."
diff mbox series

Patch

diff --git a/meta/recipes-devtools/binutils/binutils-2.44.inc b/meta/recipes-devtools/binutils/binutils-2.44.inc
index 1aafbd5285..7b376d3676 100644
--- a/meta/recipes-devtools/binutils/binutils-2.44.inc
+++ b/meta/recipes-devtools/binutils/binutils-2.44.inc
@@ -36,5 +36,6 @@  SRC_URI = "\
      file://0013-Define-alignof-using-_Alignof-when-using-C11-or-newe.patch \
      file://0014-Remove-duplicate-pe-dll.o-entry-deom-targ_extra_ofil.patch \
      file://0015-CVE-2025-1153.patch \
+     file://0016-CVE-2025-1148.patch \
 "
 S  = "${WORKDIR}/git"
diff --git a/meta/recipes-devtools/binutils/binutils/0016-CVE-2025-1148.patch b/meta/recipes-devtools/binutils/binutils/0016-CVE-2025-1148.patch
new file mode 100644
index 0000000000..5d8b947431
--- /dev/null
+++ b/meta/recipes-devtools/binutils/binutils/0016-CVE-2025-1148.patch
@@ -0,0 +1,596 @@ 
+From d4115c2c8d447e297ae353892de89192c1996211 Mon Sep 17 00:00:00 2001
+From: Alan Modra <amodra@gmail.com>
+Date: Sat, 11 Jan 2025 16:19:09 +1030
+Subject: [PATCH] Replace xmalloc with stat_alloc in ld parser
+
+A few place dealing with ld script handling made some attempt to free
+memory, but this was generally ignored and would be quite a lot of
+work to implement.  Instead, use the stat_obstack rather than
+mallocing in many more cases.
+
+        * ldexp.c (exp_get_fill): Use stat_alloc for fill.
+        * ldfile.c (ldfile_try_open_bfd): Don't free yylval fields.
+        * ldgram.y: Replace xmalloc with stat_alloc throughout.
+        * ldlang.c (stat_memdup, stat_strdup): New functions.
+        (ldirname): Use stat_memdup.  Don't strdup ".".
+        (output_section_callback_sort): Use stat_alloc.
+        (output_section_callback_tree_to_list): Don't free.
+        (lang_memory_region_lookup): Use stat_strdup.
+        (lang_memory_region_alias): Likewise.
+        (add_excluded_libs): Use stat_alloc and stat_memdup.
+        (ldlang_add_undef, ldlang_add_require_defined): Use stat_strdup.
+        (lang_add_nocrossref, lang_leave_overlay): Use stat_alloc.
+        (realsymbol): Use stat_strdup for return value and always
+        free symbol.
+        (lang_new_vers_pattern, lang_new_vers_node): Use stat_alloc.
+        (lang_finalize_version_expr_head): Don't free.  Delete FIXME.
+        (lang_register_vers_node): Don't free.
+        (lang_add_vers_depend): Use stat_alloc.
+        (lang_do_version_exports_section): Likewise.
+        (lang_add_unique): Use stat_alloc and stat_strdup.
+        (lang_append_dynamic_list): Use stat_alloc.
+        * ldlang.h (stat_memdup, stat_strdup): Declare.
+        * ldlex.l: Replace xstrdup with stat_strdup throughout.
+        Replace xmemdup with stat_memdup too.
+        * lexsup.c (parse_args): Don't free export list or dynamic
+        list.
+
+(cherry picked from commit: d4115c2c8d447e297ae353892de89192c1996211)
+
+Upstream-Status: Backport [https://sourceware.org/cgit/binutils-gdb/commit/?id=d4115c2c8d447e297ae353892de89192c1996211]
+CVE: CVE-2025-1148
+
+Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>
+
+---
+ ld/ldexp.c  |  4 +--
+ ld/ldfile.c | 17 ++---------
+ ld/ldgram.y | 21 ++++++-------
+ ld/ldlang.c | 87 ++++++++++++++++++++++++++---------------------------
+ ld/ldlang.h |  4 +++
+ ld/ldlex.l  | 23 +++++++-------
+ ld/lexsup.c | 22 +-------------
+ 7 files changed, 74 insertions(+), 104 deletions(-)
+
+diff --git a/ld/ldexp.c b/ld/ldexp.c
+index 035cef60448..87d882e5066 100644
+--- a/ld/ldexp.c
++++ b/ld/ldexp.c
+@@ -1630,7 +1630,7 @@ exp_get_fill (etree_type *tree, fill_type *def, char *name)
+     {
+       unsigned char *dst;
+       unsigned char *s;
+-      fill = (fill_type *) xmalloc ((len + 1) / 2 + sizeof (*fill) - 1);
++      fill = stat_alloc ((len + 1) / 2 + sizeof (*fill) - 1);
+       fill->size = (len + 1) / 2;
+       dst = fill->data;
+       s = (unsigned char *) expld.result.str;
+@@ -1655,7 +1655,7 @@ exp_get_fill (etree_type *tree, fill_type *def, char *name)
+     }
+   else
+     {
+-      fill = (fill_type *) xmalloc (4 + sizeof (*fill) - 1);
++      fill = stat_alloc (4 + sizeof (*fill) - 1);
+       val = expld.result.value;
+       fill->data[0] = (val >> 24) & 0xff;
+       fill->data[1] = (val >> 16) & 0xff;
+diff --git a/ld/ldfile.c b/ld/ldfile.c
+index 12551504ae6..404af5fba14 100644
+--- a/ld/ldfile.c
++++ b/ld/ldfile.c
+@@ -438,18 +438,11 @@ ldfile_try_open_bfd (const char *attempt,
+ 			  if (token == ',')
+ 			    {
+ 			      if ((token = yylex ()) != NAME)
+-				{
+-				  free (arg1);
+-				  continue;
+-				}
++				continue;
+ 			      arg2 = yylval.name;
+ 			      if ((token = yylex ()) != ','
+ 				  || (token = yylex ()) != NAME)
+-				{
+-				  free (arg1);
+-				  free (arg2);
+-				  continue;
+-				}
++				continue;
+ 			      arg3 = yylval.name;
+ 			      token = yylex ();
+ 			    }
+@@ -468,18 +461,12 @@ ldfile_try_open_bfd (const char *attempt,
+ 			      if (strcmp (arg, lang_get_output_target ()) != 0)
+ 				skip = 1;
+ 			    }
+-			  free (arg1);
+-			  free (arg2);
+-			  free (arg3);
+ 			  break;
+ 			case NAME:
+ 			case LNAME:
+ 			case VERS_IDENTIFIER:
+ 			case VERS_TAG:
+-			  free (yylval.name);
+-			  break;
+ 			case INT:
+-			  free (yylval.bigint.str);
+ 			  break;
+ 			}
+ 		      token = yylex ();
+diff --git a/ld/ldgram.y b/ld/ldgram.y
+index 6d516db38c7..9bb98de2f0a 100644
+--- a/ld/ldgram.y
++++ b/ld/ldgram.y
+@@ -543,7 +543,7 @@ section_name_spec:
+ sect_flag_list:	NAME
+ 			{
+ 			  struct flag_info_list *n;
+-			  n = ((struct flag_info_list *) xmalloc (sizeof *n));
++			  n = stat_alloc (sizeof *n);
+ 			  if ($1[0] == '!')
+ 			    {
+ 			      n->with = without_flags;
+@@ -561,7 +561,7 @@ sect_flag_list:	NAME
+ 	|	sect_flag_list '&' NAME
+ 			{
+ 			  struct flag_info_list *n;
+-			  n = ((struct flag_info_list *) xmalloc (sizeof *n));
++			  n = stat_alloc (sizeof *n);
+ 			  if ($3[0] == '!')
+ 			    {
+ 			      n->with = without_flags;
+@@ -582,7 +582,7 @@ sect_flags:
+ 		INPUT_SECTION_FLAGS '(' sect_flag_list ')'
+ 			{
+ 			  struct flag_info *n;
+-			  n = ((struct flag_info *) xmalloc (sizeof *n));
++			  n = stat_alloc (sizeof *n);
+ 			  n->flag_list = $3;
+ 			  n->flags_initialized = false;
+ 			  n->not_with_flags = 0;
+@@ -595,7 +595,7 @@ exclude_name_list:
+ 		exclude_name_list wildcard_name
+ 			{
+ 			  struct name_list *tmp;
+-			  tmp = (struct name_list *) xmalloc (sizeof *tmp);
++			  tmp = stat_alloc (sizeof *tmp);
+ 			  tmp->name = $2;
+ 			  tmp->next = $1;
+ 			  $$ = tmp;
+@@ -604,7 +604,7 @@ exclude_name_list:
+ 		wildcard_name
+ 			{
+ 			  struct name_list *tmp;
+-			  tmp = (struct name_list *) xmalloc (sizeof *tmp);
++			  tmp = stat_alloc (sizeof *tmp);
+ 			  tmp->name = $1;
+ 			  tmp->next = NULL;
+ 			  $$ = tmp;
+@@ -615,7 +615,7 @@ section_name_list:
+ 		section_name_list opt_comma section_name_spec
+ 			{
+ 			  struct wildcard_list *tmp;
+-			  tmp = (struct wildcard_list *) xmalloc (sizeof *tmp);
++			  tmp = stat_alloc (sizeof *tmp);
+ 			  tmp->next = $1;
+ 			  tmp->spec = $3;
+ 			  $$ = tmp;
+@@ -624,7 +624,7 @@ section_name_list:
+ 		section_name_spec
+ 			{
+ 			  struct wildcard_list *tmp;
+-			  tmp = (struct wildcard_list *) xmalloc (sizeof *tmp);
++			  tmp = stat_alloc (sizeof *tmp);
+ 			  tmp->next = NULL;
+ 			  tmp->spec = $1;
+ 			  $$ = tmp;
+@@ -926,7 +926,7 @@ nocrossref_list:
+ 		{
+ 		  struct lang_nocrossref *n;
+ 
+-		  n = (struct lang_nocrossref *) xmalloc (sizeof *n);
++		  n = stat_alloc (sizeof *n);
+ 		  n->name = $1;
+ 		  n->next = $2;
+ 		  $$ = n;
+@@ -935,7 +935,7 @@ nocrossref_list:
+ 		{
+ 		  struct lang_nocrossref *n;
+ 
+-		  n = (struct lang_nocrossref *) xmalloc (sizeof *n);
++		  n = stat_alloc (sizeof *n);
+ 		  n->name = $1;
+ 		  n->next = $3;
+ 		  $$ = n;
+@@ -1225,8 +1225,7 @@ phdr_opt:
+ 		{
+ 		  struct lang_output_section_phdr_list *n;
+ 
+-		  n = ((struct lang_output_section_phdr_list *)
+-		       xmalloc (sizeof *n));
++		  n = stat_alloc (sizeof *n);
+ 		  n->name = $3;
+ 		  n->used = false;
+ 		  n->next = $1;
+diff --git a/ld/ldlang.c b/ld/ldlang.c
+index 74c0271973f..f613fc91f0a 100644
+--- a/ld/ldlang.c
++++ b/ld/ldlang.c
+@@ -188,6 +188,23 @@ stat_alloc (size_t size)
+   return obstack_alloc (&stat_obstack, size);
+ }
+ 
++void *
++stat_memdup (const void *src, size_t copy_size, size_t alloc_size)
++{
++  void *ret = obstack_alloc (&stat_obstack, alloc_size);
++  memcpy (ret, src, copy_size);
++  if (alloc_size > copy_size)
++    memset ((char *) ret + copy_size, 0, alloc_size - copy_size);
++  return ret;
++}
++
++char *
++stat_strdup (const char *str)
++{
++  size_t len = strlen (str) + 1;
++  return stat_memdup (str, len, len);
++}
++
+ /* Code for handling simple wildcards without going through fnmatch,
+    which can be expensive because of charset translations etc.  */
+ 
+@@ -277,15 +294,13 @@ static char *
+ ldirname (const char *name)
+ {
+   const char *base = lbasename (name);
+-  char *dirname;
+ 
+   while (base > name && IS_DIR_SEPARATOR (base[-1]))
+     --base;
+-  if (base == name)
+-    return strdup (".");
+-  dirname = strdup (name);
+-  dirname[base - name] = '\0';
+-  return dirname;
++  size_t len = base - name;
++  if (len == 0)
++    return ".";
++  return stat_memdup (name, len, len + 1);
+ }
+ 
+ /* If PATTERN is of the form archive:file, return a pointer to the
+@@ -733,7 +748,7 @@ output_section_callback_sort (lang_wild_statement_type *ptr,
+   if (wont_add_section_p (section, os))
+     return;
+ 
+-  node = (lang_section_bst_type *) xmalloc (sizeof (lang_section_bst_type));
++  node = stat_alloc (sizeof (*node));
+   node->left = 0;
+   node->right = 0;
+   node->section = section;
+@@ -764,8 +779,6 @@ output_section_callback_tree_to_list (lang_wild_statement_type *ptr,
+ 
+   if (tree->right)
+     output_section_callback_tree_to_list (ptr, tree->right, output);
+-
+-  free (tree);
+ }
+ 
+ 
+@@ -1454,7 +1467,7 @@ lang_memory_region_lookup (const char *const name, bool create)
+ 
+   new_region = stat_alloc (sizeof (lang_memory_region_type));
+ 
+-  new_region->name_list.name = xstrdup (name);
++  new_region->name_list.name = stat_strdup (name);
+   new_region->name_list.next = NULL;
+   new_region->next = NULL;
+   new_region->origin_exp = NULL;
+@@ -1509,7 +1522,7 @@ lang_memory_region_alias (const char *alias, const char *region_name)
+ 
+   /* Add alias to region name list.  */
+   n = stat_alloc (sizeof (lang_memory_region_name));
+-  n->name = xstrdup (alias);
++  n->name = stat_strdup (alias);
+   n->next = region->name_list.next;
+   region->name_list.next = n;
+ }
+@@ -2989,11 +3002,9 @@ add_excluded_libs (const char *list)
+       end = strpbrk (p, ",:");
+       if (end == NULL)
+ 	end = p + strlen (p);
+-      entry = (struct excluded_lib *) xmalloc (sizeof (*entry));
++      entry = stat_alloc (sizeof (*entry));
+       entry->next = excluded_libs;
+-      entry->name = (char *) xmalloc (end - p + 1);
+-      memcpy (entry->name, p, end - p);
+-      entry->name[end - p] = '\0';
++      entry->name = stat_memdup (p, end - p, end - p + 1);
+       excluded_libs = entry;
+       if (*end == '\0')
+ 	break;
+@@ -4017,7 +4028,7 @@ ldlang_add_undef (const char *const name, bool cmdline ATTRIBUTE_UNUSED)
+   new_undef->next = ldlang_undef_chain_list_head;
+   ldlang_undef_chain_list_head = new_undef;
+ 
+-  new_undef->name = xstrdup (name);
++  new_undef->name = stat_strdup (name);
+ 
+   if (link_info.output_bfd != NULL)
+     insert_undefined (new_undef->name);
+@@ -4096,7 +4107,7 @@ ldlang_add_require_defined (const char *const name)
+   ldlang_add_undef (name, true);
+   ptr = stat_alloc (sizeof (*ptr));
+   ptr->next = require_defined_symbol_list;
+-  ptr->name = strdup (name);
++  ptr->name = stat_strdup (name);
+   require_defined_symbol_list = ptr;
+ }
+ 
+@@ -9183,7 +9194,7 @@ lang_add_nocrossref (lang_nocrossref_type *l)
+ {
+   struct lang_nocrossrefs *n;
+ 
+-  n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
++  n = stat_alloc (sizeof *n);
+   n->next = nocrossref_list;
+   n->list = l;
+   n->onlyfirst = false;
+@@ -9366,7 +9377,7 @@ lang_leave_overlay (etree_type *lma_expr,
+ 	{
+ 	  lang_nocrossref_type *nc;
+ 
+-	  nc = (lang_nocrossref_type *) xmalloc (sizeof *nc);
++	  nc = stat_alloc (sizeof *nc);
+ 	  nc->name = l->os->name;
+ 	  nc->next = nocrossref;
+ 	  nocrossref = nc;
+@@ -9546,13 +9557,10 @@ realsymbol (const char *pattern)
+   if (changed)
+     {
+       *s = '\0';
+-      return symbol;
+-    }
+-  else
+-    {
+-      free (symbol);
+-      return pattern;
++      pattern = stat_strdup (symbol);
+     }
++  free (symbol);
++  return pattern;
+ }
+ 
+ /* This is called for each variable name or match expression.  NEW_NAME is
+@@ -9567,7 +9575,7 @@ lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
+ {
+   struct bfd_elf_version_expr *ret;
+ 
+-  ret = (struct bfd_elf_version_expr *) xmalloc (sizeof *ret);
++  ret = stat_alloc (sizeof *ret);
+   ret->next = orig;
+   ret->symver = 0;
+   ret->script = 0;
+@@ -9604,7 +9612,8 @@ lang_new_vers_node (struct bfd_elf_version_expr *globals,
+ {
+   struct bfd_elf_version_tree *ret;
+ 
+-  ret = (struct bfd_elf_version_tree *) xcalloc (1, sizeof *ret);
++  ret = stat_alloc (sizeof (*ret));
++  memset (ret, 0, sizeof (*ret));
+   ret->globals.list = globals;
+   ret->locals.list = locals;
+   ret->match = lang_vers_match;
+@@ -9686,15 +9695,7 @@ lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
+ 		    }
+ 		  while (e1 && strcmp (e1->pattern, e->pattern) == 0);
+ 
+-		  if (last == NULL)
+-		    {
+-		      /* This is a duplicate.  */
+-		      /* FIXME: Memory leak.  Sometimes pattern is not
+-			 xmalloced alone, but in larger chunk of memory.  */
+-		      /* free (e->pattern); */
+-		      free (e);
+-		    }
+-		  else
++		  if (last != NULL)
+ 		    {
+ 		      e->next = last->next;
+ 		      last->next = e;
+@@ -9734,7 +9735,6 @@ lang_register_vers_node (const char *name,
+     {
+       einfo (_("%X%P: anonymous version tag cannot be combined"
+ 	       " with other version tags\n"));
+-      free (version);
+       return;
+     }
+ 
+@@ -9827,7 +9827,7 @@ lang_add_vers_depend (struct bfd_elf_version_deps *list, const char *name)
+   struct bfd_elf_version_deps *ret;
+   struct bfd_elf_version_tree *t;
+ 
+-  ret = (struct bfd_elf_version_deps *) xmalloc (sizeof *ret);
++  ret = stat_alloc (sizeof *ret);
+   ret->next = list;
+ 
+   for (t = link_info.version_info; t != NULL; t = t->next)
+@@ -9860,7 +9860,7 @@ lang_do_version_exports_section (void)
+ 	continue;
+ 
+       len = sec->size;
+-      contents = (char *) xmalloc (len);
++      contents = stat_alloc (len);
+       if (!bfd_get_section_contents (is->the_bfd, sec, contents, 0, len))
+ 	einfo (_("%X%P: unable to read .exports section contents\n"), sec);
+ 
+@@ -9871,8 +9871,6 @@ lang_do_version_exports_section (void)
+ 	  p = strchr (p, '\0') + 1;
+ 	}
+ 
+-      /* Do not free the contents, as we used them creating the regex.  */
+-
+       /* Do not include this section in the link.  */
+       sec->flags |= SEC_EXCLUDE | SEC_KEEP;
+     }
+@@ -9936,8 +9934,8 @@ lang_add_unique (const char *name)
+     if (strcmp (ent->name, name) == 0)
+       return;
+ 
+-  ent = (struct unique_sections *) xmalloc (sizeof *ent);
+-  ent->name = xstrdup (name);
++  ent = stat_alloc (sizeof *ent);
++  ent->name = stat_strdup (name);
+   ent->next = unique_section_list;
+   unique_section_list = ent;
+ }
+@@ -9960,7 +9958,8 @@ lang_append_dynamic_list (struct bfd_elf_dynamic_list **list_p,
+     {
+       struct bfd_elf_dynamic_list *d;
+ 
+-      d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
++      d = stat_alloc (sizeof (*d));
++      memset (d, 0, sizeof (*d));
+       d->head.list = dynamic;
+       d->match = lang_vers_match;
+       *list_p = d;
+diff --git a/ld/ldlang.h b/ld/ldlang.h
+index 91779a584b4..b074d6f5e37 100644
+--- a/ld/ldlang.h
++++ b/ld/ldlang.h
+@@ -664,6 +664,10 @@ extern void lang_for_each_statement_worker
+   (void (*) (lang_statement_union_type *), lang_statement_union_type *);
+ extern void *stat_alloc
+   (size_t);
++extern void * stat_memdup
++  (const void *, size_t, size_t);
++extern char *stat_strdup
++  (const char *);
+ extern void strip_excluded_output_sections
+   (void);
+ extern void lang_clear_os_map
+diff --git a/ld/ldlex.l b/ld/ldlex.l
+index e704a979722..58eca1b2fe7 100644
+--- a/ld/ldlex.l
++++ b/ld/ldlex.l
+@@ -188,7 +188,8 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
+ 					   && (yytext[1] == 'x'
+ 					       || yytext[1] == 'X'))
+ 				    {
+-				      yylval.bigint.str = xstrdup (yytext + 2);
++				      yylval.bigint.str
++					= stat_strdup (yytext + 2);
+ 				    }
+ 				  return INT;
+ 				}
+@@ -391,32 +392,32 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
+ 
+ <MRI>{FILENAMECHAR1}{NOCFILENAMECHAR}*	{
+ /* Filename without commas, needed to parse mri stuff */
+-				  yylval.name = xstrdup (yytext);
++				  yylval.name = stat_strdup (yytext);
+ 				  return NAME;
+ 				}
+ 
+ 
+ <SCRIPT,INPUTLIST>{FILENAMECHAR1}{FILENAMECHAR}*	{
+-				  yylval.name = xstrdup (yytext);
++				  yylval.name = stat_strdup (yytext);
+ 				  return NAME;
+ 				}
+ <INPUTLIST>"="{FILENAMECHAR1}{FILENAMECHAR}*	{
+ /* Filename to be prefixed by --sysroot or when non-sysrooted, nothing.  */
+-				  yylval.name = xstrdup (yytext);
++				  yylval.name = stat_strdup (yytext);
+ 				  return NAME;
+ 				}
+ <INPUTLIST>"-l"{FILENAMECHAR}+ {
+-				  yylval.name = xstrdup (yytext + 2);
++				  yylval.name = stat_strdup (yytext + 2);
+ 				  return LNAME;
+ 				}
+ <EXPRESSION>{SYMBOLNAMECHAR1}{SYMBOLNAMECHAR}* {
+-				  yylval.name = xstrdup (yytext);
++				  yylval.name = stat_strdup (yytext);
+ 				  return NAME;
+ 				}
+   /* The following rule is to prevent a fill expression on the output
+      section before /DISCARD/ interpreting the '/' as a divide.  */
+ <EXPRESSION>"/DISCARD/"		{
+-				  yylval.name = xstrdup (yytext);
++				  yylval.name = stat_strdup (yytext);
+ 				  return NAME;
+ 				}
+ <WILD>{WILDCHAR}* {
+@@ -431,14 +432,14 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
+ 		  }
+ 		else
+ 		  {
+-		    yylval.name = xstrdup (yytext);
++		    yylval.name = stat_strdup (yytext);
+ 		    return NAME;
+ 		  }
+ 	}
+ 
+ <SCRIPT,EXPRESSION,WILD,VERS_NODE,INPUTLIST>"\""[^\"]*"\"" {
+ 		/* No matter the state, quotes give what's inside.  */
+-		yylval.name = xmemdup (yytext + 1, yyleng - 2, yyleng - 1);
++		yylval.name = stat_memdup (yytext + 1, yyleng - 2, yyleng - 1);
+ 		return NAME;
+ 	}
+ 
+@@ -457,10 +458,10 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
+ 
+ <VERS_NODE>extern		{ RTOKEN(EXTERN); }
+ 
+-<VERS_NODE>{V_IDENTIFIER}	{ yylval.name = xstrdup (yytext);
++<VERS_NODE>{V_IDENTIFIER}	{ yylval.name = stat_strdup (yytext);
+ 				  return VERS_IDENTIFIER; }
+ 
+-<VERS_SCRIPT>{V_TAG}		{ yylval.name = xstrdup (yytext);
++<VERS_SCRIPT>{V_TAG}		{ yylval.name = stat_strdup (yytext);
+ 				  return VERS_TAG; }
+ 
+ <VERS_START>"{"			{ BEGIN(VERS_SCRIPT); return *yytext; }
+diff --git a/ld/lexsup.c b/ld/lexsup.c
+index 5399aa45b72..58b9bdd4974 100644
+--- a/ld/lexsup.c
++++ b/ld/lexsup.c
+@@ -1989,16 +1989,6 @@ parse_args (unsigned argc, char **argv)
+ 	  if (opt_dynamic_list != dynamic_list_data)
+ 	    opt_dynamic_list = dynamic_list;
+ 	}
+-      else
+-	{
+-	  /* Free the export list.  */
+-	  for (; head->next != NULL; head = next)
+-	    {
+-	      next = head->next;
+-	      free (head);
+-	    }
+-	  free (export_list);
+-	}
+     }
+ 
+   switch (opt_dynamic_list)
+@@ -2022,17 +2012,7 @@ parse_args (unsigned argc, char **argv)
+ 	break;
+       case symbolic:
+ 	link_info.symbolic = true;
+-	if (link_info.dynamic_list)
+-	  {
+-	    struct bfd_elf_version_expr *ent, *next;
+-	    for (ent = link_info.dynamic_list->head.list; ent; ent = next)
+-	      {
+-		next = ent->next;
+-		free (ent);
+-	      }
+-	    free (link_info.dynamic_list);
+-	    link_info.dynamic_list = NULL;
+-	  }
++	link_info.dynamic_list = NULL;
+ 	break;
+       case symbolic_functions:
+ 	link_info.dynamic = true;