Sanity check elf_sym_hashes indexing

Alan Modra amodra@gmail.com
Tue Oct 21 23:48:27 GMT 2025


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 --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index e25e2fca2a6..687e699fa80 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -904,6 +904,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).  */
@@ -3180,7 +3182,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 77c64e2f729..0c521c09722 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 ca0b41a56d3..bddeab142db 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 *
@@ -9165,6 +9167,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)
 {
@@ -9197,7 +9200,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)
     {
       /* FIXMEL What should we do ?  */
@@ -11415,6 +11418,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;
@@ -11439,9 +11443,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
@@ -11684,7 +11689,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
@@ -11830,7 +11835,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
@@ -11906,8 +11911,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;
@@ -12072,7 +12077,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 ?  */
@@ -13905,9 +13910,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 ?  */

-- 
Alan Modra


More information about the Binutils mailing list