[v2,2_44] Backport Replace xmalloc with stat_alloc in ld parser

Message ID 20250313113312.3398702-1-Harish.Sadineni@windriver.com
State New
Headers
Series [v2,2_44] Backport Replace xmalloc with stat_alloc in ld parser |

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 13, 2025, 11:33 a.m. UTC
  From: Alan Modra <amodra@gmail.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.

    * 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.

(Backporting from master: d4115c2c8d447e297ae353892de89192c1996211)

Fixes https://sourceware.org/bugzilla/show_bug.cgi?id=32576

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

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

Alan Modra March 13, 2025, 11:40 p.m. UTC | #1
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.

If you are interested in fixing memory leaks, I recommend you work on
the master branch.  The same goes for any bugs exposed by fuzzing.
  
Jan Beulich March 14, 2025, 11:06 a.m. UTC | #2
On 13.03.2025 12:33, Harish.Sadineni@windriver.com wrote:
> From: Alan Modra <amodra@gmail.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.
> 
>     * 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.
> 
> (Backporting from master: d4115c2c8d447e297ae353892de89192c1996211)
> 
> Fixes https://sourceware.org/bugzilla/show_bug.cgi?id=32576
> 
> 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
> 
> Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>

Mind me asking what the purpose of this re-submission is? You were told
already that this kind of work is not wanted to be put on branches.
Unless of course you can point out an issue it fixes beyond the CVEs
that you list, all of which aren't really an issue afaict.

Jan
  
Jan Beulich March 14, 2025, 11:07 a.m. UTC | #3
On 14.03.2025 12:06, Jan Beulich wrote:
> On 13.03.2025 12:33, Harish.Sadineni@windriver.com wrote:
>> From: Alan Modra <amodra@gmail.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.
>>
>>     * 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.
>>
>> (Backporting from master: d4115c2c8d447e297ae353892de89192c1996211)
>>
>> Fixes https://sourceware.org/bugzilla/show_bug.cgi?id=32576
>>
>> 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
>>
>> Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>
> 
> Mind me asking what the purpose of this re-submission is? You were told
> already that this kind of work is not wanted to be put on branches.
> Unless of course you can point out an issue it fixes beyond the CVEs
> that you list, all of which aren't really an issue afaict.

Oh, I'm sorry - it was this v2 that Alan did respond to. Just that this v2
appeared in my inbox only now.

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;