[PATCH][Binutils][2_42] Backport fixes for (PR 32636, 32641, 32643, 32644)

sunilkumar.dora@windriver.com sunilkumar.dora@windriver.com
Tue Apr 29 14:52:37 GMT 2025


From: Nick Clifton <nickc@redhat.com>

This commit backports the following fixes from master to the 2.42 branch:

Cherry pick from master:

1. PR 32636 - Fix heap-buffer-overflow in _bfd_elf_gc_mark_rsec when processing
   corrupt ELF input with long paths and --gc-sections. Prevents illegal memory
   access when indexing sym_hashes array.
   (https://sourceware.org/bugzilla/show_bug.cgi?id=32636)

2. PR 32641 - Fix SEGV in _bfd_x86_elf_check_relocs when processing malformed
   relocations with -w option. Adds validation for relocation/symbol integrity.
   (https://sourceware.org/bugzilla/show_bug.cgi?id=32641)

3. PR 32643 - Fix SEGV in _bfd_elf_gc_mark_rsec with --gc-sections on corrupt
   input. Strengthens relocation processing checks.
   (https://sourceware.org/bugzilla/show_bug.cgi?id=32643)

4. PR 32644 - Fix SEGV in bfd_elf_reloc_symbol_deleted_p with combination of
   --no-undefined, --orphan-handling and -w options. Adds missing validation.
   (https://sourceware.org/bugzilla/show_bug.cgi?id=32644)

Signed-off-by: Sunil Dora <sunilkumar.dora@windriver.com>
---
 bfd/elf-bfd.h      |   3 +
 bfd/elf64-x86-64.c |  10 +-
 bfd/elflink.c      | 270 ++++++++++++++++++++++++---------------------
 bfd/elfxx-x86.c    |  20 ++--
 4 files changed, 162 insertions(+), 141 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 3ed22fa6c52..07add7d0302 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -3126,6 +3126,9 @@ extern bool _bfd_elf_maybe_set_textrel
 extern bool _bfd_elf_add_dynamic_tags
   (bfd *, struct bfd_link_info *, bool);
 
+extern struct elf_link_hash_entry * _bfd_elf_get_link_hash_entry
+  (struct elf_link_hash_entry **, unsigned int, Elf_Internal_Shdr *);
+
 /* Large common section.  */
 extern asection _bfd_elf_large_com_section;
 
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 2ed120af780..74ff749c091 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1665,7 +1665,7 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
   bool to_reloc_pc32;
   bool abs_symbol;
   bool local_ref;
-  asection *tsec;
+  asection *tsec = NULL;
   bfd_signed_vma raddend;
   unsigned int opcode;
   unsigned int modrm;
@@ -1831,6 +1831,9 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
 	return true;
     }
 
+  if (tsec == NULL)
+    return false;
+
   /* Don't convert GOTPCREL relocation against large section.  */
   if (elf_section_data (tsec) !=  NULL
       && (elf_section_flags (tsec) & SHF_X86_64_LARGE) != 0)
@@ -2127,10 +2130,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
       else
 	{
 	  isym = NULL;
-	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	  while (h->root.type == bfd_link_hash_indirect
-		 || h->root.type == bfd_link_hash_warning)
-	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	  h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx, symtab_hdr);
 	}
 
       /* Check invalid x32 relocations.  */
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 0fd06d11c24..97555b166d6 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -49,22 +49,60 @@ struct elf_info_failed
 static bool _bfd_elf_fix_symbol_flags
   (struct elf_link_hash_entry *, struct elf_info_failed *);
 
-asection *
-_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
-			     unsigned long r_symndx,
-			     bool discard)
+static struct elf_link_hash_entry *
+get_link_hash_entry (struct elf_link_hash_entry **  sym_hashes,
+			unsigned int                   symndx,
+			unsigned int                   ext_sym_start)
+{
+  if (sym_hashes == NULL
+      /* Guard against corrupt input.  See PR 32636 for an example.  */
+      || symndx < ext_sym_start)
+	  return NULL;
+
+  struct elf_link_hash_entry *h = sym_hashes[symndx - ext_sym_start];
+
+  /* The hash might be empty.  See PR 32641 for an example of this.  */
+  if (h == NULL)
+    return NULL;
+
+  while (h->root.type == bfd_link_hash_indirect
+		  || h->root.type == bfd_link_hash_warning)
+	  h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  return h;
+}
+
+struct elf_link_hash_entry *
+_bfd_elf_get_link_hash_entry (struct elf_link_hash_entry **  sym_hashes,
+				unsigned int                   symndx,
+				Elf_Internal_Shdr *            symtab_hdr)
 {
-  if (r_symndx >= cookie->locsymcount
-      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
-    {
-      struct elf_link_hash_entry *h;
+  if (symtab_hdr == NULL)
+    return NULL;
+  return get_link_hash_entry (sym_hashes, symndx, symtab_hdr->sh_info);
+}
 
-      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+static struct elf_link_hash_entry *
+get_ext_sym_hash_from_cookie (struct elf_reloc_cookie *cookie, unsigned long r_symndx)
+{
+  if (cookie == NULL || cookie->sym_hashes == NULL)
+    return NULL;
 
-      while (h->root.type == bfd_link_hash_indirect
-	     || h->root.type == bfd_link_hash_warning)
-	h = (struct elf_link_hash_entry *) h->root.u.i.link;
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    return get_link_hash_entry (cookie->sym_hashes, r_symndx, cookie->extsymoff);
+  return NULL;
+}
+asection *
+_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
+				unsigned long r_symndx,
+				bool discard)
+{
+  struct elf_link_hash_entry *h;
 
+  h = get_ext_sym_hash_from_cookie (cookie, r_symndx);
+  if (h != NULL)
+    {
       if ((h->root.type == bfd_link_hash_defined
 	   || h->root.type == bfd_link_hash_defweak)
 	   && discarded_section (h->root.u.def.section))
@@ -72,21 +110,18 @@ _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
       else
 	return NULL;
     }
-  else
-    {
-      /* It's not a relocation against a global symbol,
-	 but it could be a relocation against a local
-	 symbol for a discarded section.  */
-      asection *isec;
-      Elf_Internal_Sym *isym;
+  /* It's not a relocation against a global symbol,
+     but it could be a relocation against a local
+     symbol for a discarded section.  */
+  asection *isec;
+  Elf_Internal_Sym *isym;
 
-      /* Need to: get the symbol; get the section.  */
-      isym = &cookie->locsyms[r_symndx];
-      isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
-      if (isec != NULL
-	  && discard ? discarded_section (isec) : 1)
-	return isec;
-     }
+  /* Need to: get the symbol; get the section.  */
+  isym = &cookie->locsyms[r_symndx];
+  isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
+  if (isec != NULL
+      && discard ? discarded_section (isec) : 1)
+    return isec;
   return NULL;
 }
 
@@ -8880,7 +8915,6 @@ set_symbol_value (bfd *bfd_with_globals,
 		  size_t symidx,
 		  bfd_vma val)
 {
-  struct elf_link_hash_entry **sym_hashes;
   struct elf_link_hash_entry *h;
   size_t extsymoff = locsymcount;
 
@@ -8904,11 +8938,12 @@ set_symbol_value (bfd *bfd_with_globals,
   /* It is a global symbol: set its link type
      to "defined" and give it a value.  */
 
-  sym_hashes = elf_sym_hashes (bfd_with_globals);
-  h = sym_hashes [symidx - extsymoff];
-  while (h->root.type == bfd_link_hash_indirect
-	 || h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+  h = get_link_hash_entry (elf_sym_hashes (bfd_with_globals), symidx, extsymoff);
+  if (h == NULL)
+    {
+      /* FIXMEL What should we do ?  */
+      return;
+    }
   h->root.type = bfd_link_hash_defined;
   h->root.u.def.value = val;
   h->root.u.def.section = bfd_abs_section_ptr;
@@ -11379,10 +11414,19 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	      || (elf_bad_symtab (input_bfd)
 		  && flinfo->sections[symndx] == NULL))
 	    {
-	      struct elf_link_hash_entry *h = sym_hashes[symndx - extsymoff];
-	      while (h->root.type == bfd_link_hash_indirect
-		     || h->root.type == bfd_link_hash_warning)
-		h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	      struct elf_link_hash_entry *h;
+
+	      h = get_link_hash_entry (sym_hashes, symndx, extsymoff);
+	      if (h == NULL)
+		{
+		  _bfd_error_handler
+		    /* xgettext:c-format */
+		    (_("error: %pB: unable to create group section symbol"),
+		     input_bfd);
+		  bfd_set_error (bfd_error_bad_value);
+		  return false;
+		}
+
 	      /* Arrange for symbol to be output.  */
 	      h->indx = -2;
 	      elf_section_data (osec)->this_hdr.sh_info = -2;
@@ -11516,7 +11560,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 		  || (elf_bad_symtab (input_bfd)
 		      && flinfo->sections[r_symndx] == NULL))
 		{
-		  h = sym_hashes[r_symndx - extsymoff];
+		  h = get_link_hash_entry (sym_hashes, r_symndx, extsymoff);
 
 		  /* Badly formatted input files can contain relocs that
 		     reference non-existant symbols.  Check here so that
@@ -11525,17 +11569,13 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 		    {
 		      _bfd_error_handler
 			/* xgettext:c-format */
-			(_("error: %pB contains a reloc (%#" PRIx64 ") for section %pA "
+			(_("error: %pB contains a reloc (%#" PRIx64 ") for section '%pA' "
 			   "that references a non-existent global symbol"),
 			 input_bfd, (uint64_t) rel->r_info, o);
 		      bfd_set_error (bfd_error_bad_value);
 		      return false;
 		    }
 
-		  while (h->root.type == bfd_link_hash_indirect
-			 || h->root.type == bfd_link_hash_warning)
-		    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
 		  s_type = h->type;
 
 		  /* If a plugin symbol is referenced from a non-IR file,
@@ -11751,7 +11791,6 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 			  && flinfo->sections[r_symndx] == NULL))
 		    {
 		      struct elf_link_hash_entry *rh;
-		      unsigned long indx;
 
 		      /* This is a reloc against a global symbol.  We
 			 have not yet output all the local symbols, so
@@ -11760,15 +11799,16 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 			 reloc to point to the global hash table entry
 			 for this symbol.  The symbol index is then
 			 set at the end of bfd_elf_final_link.  */
-		      indx = r_symndx - extsymoff;
-		      rh = elf_sym_hashes (input_bfd)[indx];
-		      while (rh->root.type == bfd_link_hash_indirect
-			     || rh->root.type == bfd_link_hash_warning)
-			rh = (struct elf_link_hash_entry *) rh->root.u.i.link;
-
-		      /* Setting the index to -2 tells
-			 elf_link_output_extsym that this symbol is
-			 used by a reloc.  */
+		      rh = get_link_hash_entry (elf_sym_hashes (input_bfd),
+						r_symndx, extsymoff);
+		      if (rh == NULL)
+			{
+			  /* FIXME: Generate an error ?  */
+			  continue;
+			}
+
+		      /* Setting the index to -2 tells elf_link_output_extsym
+			 that this symbol is used by a reloc.  */
 		      BFD_ASSERT (rh->indx < 0);
 		      rh->indx = -2;
 		      *rel_hash = rh;
@@ -13732,25 +13772,21 @@ _bfd_elf_gc_mark_hook (asection *sec,
 		       struct elf_link_hash_entry *h,
 		       Elf_Internal_Sym *sym)
 {
-  if (h != NULL)
+  if (h == NULL)
+    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+
+  switch (h->root.type)
     {
-      switch (h->root.type)
-	{
-	case bfd_link_hash_defined:
-	case bfd_link_hash_defweak:
-	  return h->root.u.def.section;
+    case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
+      return h->root.u.def.section;
 
-	case bfd_link_hash_common:
-	  return h->root.u.c.p->section;
+    case bfd_link_hash_common:
+      return h->root.u.c.p->section;
 
-	default:
-	  break;
-	}
+    default:
+      return NULL;
     }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-
-  return NULL;
 }
 
 /* Return the debug definition section.  */
@@ -13799,56 +13835,45 @@ _bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
   if (r_symndx == STN_UNDEF)
     return NULL;
 
-  if (r_symndx >= cookie->locsymcount
-      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+  h = get_ext_sym_hash_from_cookie (cookie, r_symndx);
+  if (h == NULL)
     {
-      bool was_marked;
+      /* A corrup tinput file can lead to a situation where the index
+	 does not reference either a local or an external symbol.  */
+      if (r_symndx >= cookie->locsymcount)
+       return NULL;
 
-      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
-      if (h == NULL)
-	{
-	  info->callbacks->einfo (_("%F%P: corrupt input: %pB\n"),
-				  sec->owner);
-	  return NULL;
-	}
-      while (h->root.type == bfd_link_hash_indirect
-	     || h->root.type == bfd_link_hash_warning)
-	h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
-      was_marked = h->mark;
-      h->mark = 1;
-      /* Keep all aliases of the symbol too.  If an object symbol
-	 needs to be copied into .dynbss then all of its aliases
-	 should be present as dynamic symbols, not just the one used
-	 on the copy relocation.  */
-      hw = h;
-      while (hw->is_weakalias)
-	{
-	  hw = hw->u.alias;
-	  hw->mark = 1;
-	}
-
-      if (!was_marked && h->start_stop && !h->root.ldscript_def)
-	{
-	  if (info->start_stop_gc)
-	    return NULL;
-
-	  /* To work around a glibc bug, mark XXX input sections
-	     when there is a reference to __start_XXX or __stop_XXX
-	     symbols.  */
-	  else if (start_stop != NULL)
-	    {
-	      asection *s = h->u2.start_stop_section;
-	      *start_stop = true;
-	      return s;
-	    }
-	}
-
-      return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+      return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+		      &cookie->locsyms[r_symndx]);
     }
+  bool was_marked = h->mark;
+  h->mark = 1;
+  /* Keep all aliases of the symbol too.  If an object symbol
+     needs to be copied into .dynbss then all of its aliases
+     should be present as dynamic symbols, not just the one used
+     on the copy relocation.  */
+  hw = h;
+  while (hw->is_weakalias)
+    {
+      hw = hw->u.alias;
+      hw->mark = 1;
+    }
+  if (!was_marked && h->start_stop && !h->root.ldscript_def)
+    {
+      if (info->start_stop_gc)
+       return NULL;
 
-  return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
-			  &cookie->locsyms[r_symndx]);
+      /* To work around a glibc bug, mark XXX input sections
+	 when there is a reference to __start_XXX or __stop_XXX
+	 symbols.  */
+      else if (start_stop != NULL)
+       {
+	 asection *s = h->u2.start_stop_section;
+	 *start_stop = true;
+	 return s;
+       }
+    }
+  return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
 }
 
 /* COOKIE->rel describes a relocation against section SEC, which is
@@ -14860,17 +14885,11 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
       if (r_symndx == STN_UNDEF)
 	return true;
 
-      if (r_symndx >= rcookie->locsymcount
-	  || ELF_ST_BIND (rcookie->locsyms[r_symndx].st_info) != STB_LOCAL)
-	{
-	  struct elf_link_hash_entry *h;
-
-	  h = rcookie->sym_hashes[r_symndx - rcookie->extsymoff];
-
-	  while (h->root.type == bfd_link_hash_indirect
-		 || h->root.type == bfd_link_hash_warning)
-	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      struct elf_link_hash_entry *h;
 
+      h = get_ext_sym_hash_from_cookie (rcookie, r_symndx);
+      if (h != NULL)
+	{
 	  if ((h->root.type == bfd_link_hash_defined
 	       || h->root.type == bfd_link_hash_defweak)
 	      && (h->root.u.def.section->owner != rcookie->abfd
@@ -14880,6 +14899,10 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
 	}
       else
 	{
+	  if (r_symndx >= rcookie->locsymcount)
+	  /* This can happen with corrupt input.  */
+	    return false;
+
 	  /* It's not a relocation against a global symbol,
 	     but it could be a relocation against a local
 	     symbol for a discarded section.  */
@@ -14894,6 +14917,7 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
 		  || discarded_section (isec)))
 	    return true;
 	}
+
       return false;
     }
   return false;
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 508fd771da3..8c261cf840a 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -972,15 +972,7 @@ _bfd_x86_elf_check_relocs (bfd *abfd,
 	  goto error_return;
 	}
 
-      if (r_symndx < symtab_hdr->sh_info)
-	h = NULL;
-      else
-	{
-	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	  while (h->root.type == bfd_link_hash_indirect
-		 || h->root.type == bfd_link_hash_warning)
-	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-	}
+      h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx, symtab_hdr);
 
       if (X86_NEED_DYNAMIC_RELOC_TYPE_P (is_x86_64, r_type)
 	  && NEED_DYNAMIC_RELOCATION_P (is_x86_64, info, true, h, sec,
@@ -1205,10 +1197,12 @@ _bfd_x86_elf_link_relax_section (bfd *abfd ATTRIBUTE_UNUSED,
       else
 	{
 	  /* Get H and SEC for GENERATE_DYNAMIC_RELOCATION_P below.  */
-	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	  while (h->root.type == bfd_link_hash_indirect
-		 || h->root.type == bfd_link_hash_warning)
-	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	  h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx, symtab_hdr);
+	  if (h == NULL)
+	    {
+	      /* FIXMEL: Issue an error message ?  */
+	      continue;
+	    }
 
 	  if (h->root.type == bfd_link_hash_defined
 	      || h->root.type == bfd_link_hash_defweak)
-- 
2.43.0



More information about the Binutils mailing list