[2_44] Backport Replace xmalloc with stat_alloc in ld parser (CVE-2025-1148)

Message ID 20250312070610.3937378-1-Harish.Sadineni@windriver.com
State New
Headers
Series [2_44] Backport Replace xmalloc with stat_alloc in ld parser (CVE-2025-1148) |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Sadineni, Harish March 12, 2025, 7:06 a.m. UTC
  From: Harish Sadineni <Harish.Sadineni@windriver.com>

commit d4115c2c8d447e297ae353892de89192c1996211
Author: Alan Modra <amodra@gmail.com>
Date:   Sat Jan 11 16:19:09 2025 +1030

    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 pick from master: d4115c2c8d447e297ae353892de89192c1996211)

Fixes https://sourceware.org/bugzilla/show_bug.cgi?id=32576 (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(-)
  

Comments

Jan Beulich March 13, 2025, 8:20 a.m. UTC | #1
On 12.03.2025 08:06, Harish.Sadineni@windriver.com wrote:
> From: Harish Sadineni <Harish.Sadineni@windriver.com>

This isn't quite right, is it? The author of a backport is usually still ...

> commit d4115c2c8d447e297ae353892de89192c1996211
> Author: Alan Modra <amodra@gmail.com>

... the original author.

> Date:   Sat Jan 11 16:19:09 2025 +1030
> 
>     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 pick from master: d4115c2c8d447e297ae353892de89192c1996211)
> 
> Fixes https://sourceware.org/bugzilla/show_bug.cgi?id=32576 (CVE-2025-1148)
> 
> Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>

As indicated when the more general question was raised, I think what's missing
here is a (good) justification for putting this on a stable branch. It's a
relatively large change for a relatively low priority bug. Unless of course
you know facts beyond what is stated there. Without extra justification
personally I'd reject this; I'm happy for Nick or Alan to override me, though.

Jan
  
Sadineni, Harish March 13, 2025, 9:39 a.m. UTC | #2
Hi Jan Beulich,

>>This isn't quite right, is it? The author of a backport is usually still ...
>>... the original author.

Alright, I will add the "Signed-off-by: Modra amodra@gmail.com" before mine.

>>As indicated when the more general question was raised, I think what's missing
>>here is a (good) justification for putting this on a stable branch. It's a
>>relatively large change for a relatively low priority bug. Unless of course
>>you know facts beyond what is stated there. Without extra justification
>>personally I'd reject this; I'm happy for Nick or Alan to override me, though.

Since we are backporting, I have used the same commit message. Could you kindly clarify what type of justification should be added?.
There are four CVEs associated with this bug (32576):
https://nvd.nist.gov/vuln/detail/CVE-2025-1148
https://nvd.nist.gov/vuln/detail/CVE-2025-1150
https://nvd.nist.gov/vuln/detail/CVE-2025-1151
https://nvd.nist.gov/vuln/detail/CVE-2025-1152

Thanks
Harish
  
Xi Ruoyao March 13, 2025, 9:53 a.m. UTC | #3
On Thu, 2025-03-13 at 09:39 +0000, Sadineni, Harish wrote:
> 
> Hi Jan Beulich,
> 
> > > This isn't quite right, is it? The author of a backport is usually
> > > still ...
> > > ... the original author.
> 
> 
> 
> Alright, I will add the "Signed-off-by: Modra amodra@gmail.com" before
> mine. 

No it's not correct.  You need "git commit --author=...".

You cannot add Signed-off-by: from another person unless (s)he
explicitly agrees.  It's analogous to formally signing on a paper.
  
Jan Beulich March 13, 2025, 10:40 a.m. UTC | #4
On 13.03.2025 10:39, Sadineni, Harish wrote:
>>> This isn't quite right, is it? The author of a backport is usually still ...
>>> ... the original author.
> 
> Alright, I will add the "Signed-off-by: Modra amodra@gmail.com" before mine.

I don't think you may take an S-o-b out of thin air. It's From: that you want
to adjust.

>>> As indicated when the more general question was raised, I think what's missing
>>> here is a (good) justification for putting this on a stable branch. It's a
>>> relatively large change for a relatively low priority bug. Unless of course
>>> you know facts beyond what is stated there. Without extra justification
>>> personally I'd reject this; I'm happy for Nick or Alan to override me, though.
> 
> Since we are backporting, I have used the same commit message. Could you kindly clarify what type of justification should be added?.

See my earlier reply on the matter: A post-commit-message remark may be what's
best to use in such a case. However, ...

> There are four CVEs associated with this bug (32576):
> https://nvd.nist.gov/vuln/detail/CVE-2025-1148
> https://nvd.nist.gov/vuln/detail/CVE-2025-1150
> https://nvd.nist.gov/vuln/detail/CVE-2025-1151
> https://nvd.nist.gov/vuln/detail/CVE-2025-1152

... there being whatever number of CVEs isn't a sufficient justification, imo.
CVEs, sadly, vary greatly in how important (and sometimes even relevant) it is
that they cover.

Jan
  

Patch

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 88d2c188fac..cc60ca0d493 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;
 }
 
@@ -9184,7 +9195,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;
@@ -9367,7 +9378,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;
@@ -9547,13 +9558,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
@@ -9568,7 +9576,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;
@@ -9605,7 +9613,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;
@@ -9687,15 +9696,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;
@@ -9735,7 +9736,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;
     }
 
@@ -9828,7 +9828,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)
@@ -9861,7 +9861,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);
 
@@ -9872,8 +9872,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;
     }
@@ -9937,8 +9935,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;
 }
@@ -9961,7 +9959,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;