[binutils-gdb] Sanity check elf_sym_hashes indexing

Alan Modra amodra@sourceware.org
Thu Oct 30 05:57:46 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4b738eecc008099574fb8263b2e1fac758eedf02

commit 4b738eecc008099574fb8263b2e1fac758eedf02
Author: Alan Modra <amodra@gmail.com>
Date:   Thu Oct 30 16:26:57 2025 +1030

    Sanity check elf_sym_hashes indexing
    
    I'm a little surprised we haven't already had fuzzing reports of
    indexing off the end of sym_hashes.  The idea here is to preempt such
    bugs.  One wrinkle is that ppc64 can't leave a zero symtab_hdr when
    setting up sym_hashes for the fake stub bfd.
    
            * elf-bfd.h (struct elf_reloc_cookie): Add "num_sym".
            (_bfd_elf_get_link_hash_entry): Update declaration.
            * elf-eh-frame.c (find_merged_cie): Sanity check reloc symbol
            index.
            * elf64-ppc.c (use_global_in_relocs): Fake up symtab_hdr for
            stub bfd.
            * elflink.c (_bfd_elf_get_link_hash_entry): Add "num_sym"
            param.  Check symndx against it.  Update all calls.
            (set_symbol_value): Add "num_sym" param and update all calls.
            (elf_link_input_bfd): Add "num_syms" var and use for above.
            (init_reloc_cookie): Set "cookie->num_syms".
            * elf64-x86-64.c (elf_x86_64_scan_relocs): Pass symtab number
            of entries to _bfd_elf_get_link_hash_entry.
            * elfxx-x86.c (_bfd_x86_elf_check_relocs): Likewise.
            (_bfd_x86_elf_link_relax_section): Likewise.

Diff:
---
 bfd/elf-bfd.h      |  4 +++-
 bfd/elf-eh-frame.c |  2 ++
 bfd/elf64-ppc.c    |  3 +++
 bfd/elf64-x86-64.c |  3 ++-
 bfd/elflink.c      | 28 +++++++++++++++++-----------
 bfd/elfxx-x86.c    |  6 ++++--
 6 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index e51bfcf6360..372203dd33c 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -901,6 +901,8 @@ struct elf_reloc_cookie
 {
   bfd *abfd;
   Elf_Internal_Rela *rels, *rel, *relend;
+  /* Number of symbols in .symtab.  */
+  unsigned int num_sym;
   /* Number of symbols that may be local syms (all when bad_symtab).  */
   unsigned int locsymcount;
   /* Symbol index of first possible global sym (0 when bad_symtab).  */
@@ -3172,7 +3174,7 @@ extern void _bfd_elf_link_munmap_section_contents
   (asection *);
 
 extern struct elf_link_hash_entry * _bfd_elf_get_link_hash_entry
-  (struct elf_link_hash_entry **, unsigned int, unsigned int);
+  (struct elf_link_hash_entry **, unsigned int, unsigned int, unsigned int);
 extern asection *_bfd_get_local_sym_section
   (struct elf_reloc_cookie *, unsigned int);
 
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 97ba1a3dce6..0212523cc79 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -1256,6 +1256,8 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
 #endif
 	r_symndx = ELF32_R_SYM (rel->r_info);
 
+      if (r_symndx > cookie->num_sym)
+	return cie_inf;
       h = NULL;
       if (r_symndx >= cookie->extsymoff)
 	h = elf_sym_hashes (cookie->abfd)[r_symndx - cookie->extsymoff];
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index b706cf4d438..205cf1511cb 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -11641,6 +11641,9 @@ use_global_in_relocs (struct ppc_link_hash_table *htab,
       if (hashes == NULL)
 	return false;
       elf_sym_hashes (htab->params->stub_bfd) = hashes;
+      Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (htab->params->stub_bfd);
+      symtab_hdr->sh_entsize = sizeof (Elf64_External_Sym);
+      symtab_hdr->sh_size = (htab->stub_globals + 1) * symtab_hdr->sh_entsize;
       htab->stub_globals = 1;
     }
   symndx = htab->stub_globals++;
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index f2a172662a5..a4a5ebf595e 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -2616,7 +2616,8 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
 	{
 	  isym = NULL;
 	  h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx,
-					    symtab_hdr->sh_info);
+					    symtab_hdr->sh_info,
+					    NUM_SHDR_ENTRIES (symtab_hdr));
 	}
 
       /* Check invalid x32 relocations.  */
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 989a7a0a6af..0ea974d45ce 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -96,11 +96,13 @@ _bfd_elf_link_keep_memory (struct bfd_link_info *info)
 struct elf_link_hash_entry *
 _bfd_elf_get_link_hash_entry (struct elf_link_hash_entry **sym_hashes,
 			      unsigned int symndx,
-			      unsigned int ext_sym_start)
+			      unsigned int ext_sym_start,
+			      unsigned int num_sym)
 {
   if (sym_hashes == NULL
       /* Guard against corrupt input.  See PR 32636 for an example.  */
-      || symndx < ext_sym_start)
+      || symndx < ext_sym_start
+      || symndx >= num_sym)
     return NULL;
 
   struct elf_link_hash_entry *h = sym_hashes[symndx - ext_sym_start];
@@ -124,7 +126,7 @@ get_ext_sym_hash_from_cookie (struct elf_reloc_cookie *cookie,
     return NULL;
 
   return _bfd_elf_get_link_hash_entry (elf_sym_hashes (cookie->abfd), symndx,
-				       cookie->extsymoff);
+				       cookie->extsymoff, cookie->num_sym);
 }
 
 asection *
@@ -9108,6 +9110,7 @@ static bool
 set_symbol_value (bfd *bfd_with_globals,
 		  Elf_Internal_Sym *isymbuf,
 		  size_t locsymcount,
+		  size_t num_sym,
 		  size_t symidx,
 		  bfd_vma val)
 {
@@ -9140,7 +9143,7 @@ set_symbol_value (bfd *bfd_with_globals,
   /* It is a global symbol: set its link type
      to "defined" and give it a value.  */
   h = _bfd_elf_get_link_hash_entry (elf_sym_hashes (bfd_with_globals), symidx,
-				    extsymoff);
+				    extsymoff, num_sym);
   if (h == NULL)
     return false;
   h->root.type = bfd_link_hash_defined;
@@ -11355,6 +11358,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
   Elf_Internal_Shdr *symtab_hdr;
   size_t locsymcount;
   size_t extsymoff;
+  size_t num_sym;
   Elf_Internal_Sym *isymbuf;
   Elf_Internal_Sym *isym;
   Elf_Internal_Sym *isymend;
@@ -11379,9 +11383,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
     return true;
 
   symtab_hdr = &elf_symtab_hdr (input_bfd);
+  num_sym = symtab_hdr->sh_size / bed->s->sizeof_sym;
   if (elf_bad_symtab (input_bfd))
     {
-      locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+      locsymcount = num_sym;
       extsymoff = 0;
     }
   else
@@ -11622,7 +11627,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	      struct elf_link_hash_entry *h;
 
 	      h = _bfd_elf_get_link_hash_entry (sym_hashes, symndx,
-						extsymoff);
+						extsymoff, num_sym);
 	      if (h == NULL)
 		{
 		  _bfd_error_handler
@@ -11768,7 +11773,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 		      && flinfo->sections[r_symndx] == NULL))
 		{
 		  h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx,
-						    extsymoff);
+						    extsymoff, num_sym);
 
 		  /* Badly formatted input files can contain relocs that
 		     reference non-existant symbols.  Check here so that
@@ -11844,8 +11849,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 		    return false;
 
 		  /* Symbol evaluated OK.  Update to absolute value.  */
-		  if (!set_symbol_value (input_bfd, isymbuf, locsymcount, r_symndx,
-					 val))
+		  if (!set_symbol_value (input_bfd, isymbuf, locsymcount,
+					 num_sym, r_symndx, val))
 		    return false;
 
 		  continue;
@@ -12010,7 +12015,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 			 for this symbol.  The symbol index is then
 			 set at the end of bfd_elf_final_link.  */
 		      rh = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx,
-							 extsymoff);
+							 extsymoff, num_sym);
 		      if (rh == NULL)
 			{
 			  /* FIXME: Generate an error ?  */
@@ -13841,9 +13846,10 @@ init_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
 
   cookie->abfd = abfd;
+  cookie->num_sym = symtab_hdr->sh_size / bed->s->sizeof_sym;
   if (elf_bad_symtab (abfd))
     {
-      cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+      cookie->locsymcount = cookie->num_sym;
       cookie->extsymoff = 0;
     }
   else
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index cffec0f5aab..7a54722bd27 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -967,7 +967,8 @@ _bfd_x86_elf_check_relocs (bfd *abfd,
 	}
 
       h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx,
-					symtab_hdr->sh_info);
+					symtab_hdr->sh_info,
+					NUM_SHDR_ENTRIES (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,
@@ -1196,7 +1197,8 @@ _bfd_x86_elf_link_relax_section (bfd *abfd ATTRIBUTE_UNUSED,
 	{
 	  /* Get H and SEC for GENERATE_DYNAMIC_RELOCATION_P below.  */
 	  h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx,
-					    symtab_hdr->sh_info);
+					    symtab_hdr->sh_info,
+					    NUM_SHDR_ENTRIES (symtab_hdr));
 	  if (h == NULL)
 	    {
 	      /* FIXMEL: Issue an error message ?  */


More information about the Binutils-cvs mailing list