From 315350be6598235df12a0190a5a4c21447eead36 Mon Sep 17 00:00:00 2001
From: Nick Clifton <nickc@redhat.com>
Date: Mon, 9 May 2016 17:31:07 +0100
Subject: [PATCH] Fix seg fault objdumping a corrupt binary with an invalid
 sh_link field.

	PR binutils/20063
	* elf.c (bfd_elf_get_elf_syms): Check for out of range sh_link
	field before accessing sections array.

	* readelf.c (get_32bit_section_headers): Warn if an out of range
	sh_link or sh_info field is encountered.
	(get_64bit_section_headers): Likewise.
---
 bfd/ChangeLog      |  6 ++++++
 bfd/elf.c          | 16 +++++++++++-----
 binutils/ChangeLog |  7 +++++++
 binutils/readelf.c |  8 ++++++++
 4 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index d5edec45885..29dad0121d3 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,9 @@
+2016-05-09  Nick Clifton  <nickc@redhat.com>
+
+	PR binutils/20063
+	* elf.c (bfd_elf_get_elf_syms): Check for out of range sh_link
+	field before accessing sections array.
+
 2016-05-09  Christophe Monat  <christophe.monat@st.com>
 
 	PR ld/20030
diff --git a/bfd/elf.c b/bfd/elf.c
index 4be7d739739..1592183fae7 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -407,11 +407,17 @@ bfd_elf_get_elf_syms (bfd *ibfd,
 
       /* Find an index section that is linked to this symtab section.  */
       for (entry = elf_symtab_shndx_list (ibfd); entry != NULL; entry = entry->next)
-	if (sections[entry->hdr.sh_link] == symtab_hdr)
-	  {
-	    shndx_hdr = & entry->hdr;
-	    break;
-	  };
+	{
+	  /* PR 20063.  */
+	  if (entry->hdr.sh_link >= elf_numsections (ibfd))
+	    continue;
+
+	  if (sections[entry->hdr.sh_link] == symtab_hdr)
+	    {
+	      shndx_hdr = & entry->hdr;
+	      break;
+	    };
+	}
 
       if (shndx_hdr == NULL)
 	{
diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index 12e8bb2dac4..89ebe1573ff 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -1,3 +1,10 @@
+2016-05-09  Nick Clifton  <nickc@redhat.com>
+
+	PR binutils/20063
+	* readelf.c (get_32bit_section_headers): Warn if an out of range
+	sh_link or sh_info field is encountered.
+	(get_64bit_section_headers): Likewise.
+
 2016-05-04  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
 
 	* testsuite/lib/binutils-common.exp (is_elf_format): Add avr-*-*.
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 3d31b38bf38..13e7751c8ed 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -5059,6 +5059,10 @@ get_32bit_section_headers (FILE * file, bfd_boolean probe)
       internal->sh_info      = BYTE_GET (shdrs[i].sh_info);
       internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
       internal->sh_entsize   = BYTE_GET (shdrs[i].sh_entsize);
+      if (!probe && internal->sh_link > num)
+	warn (_("Section %u has an out of range sh_link value of %u\n"), i, internal->sh_link);
+      if (!probe && internal->sh_flags & SHF_INFO_LINK && internal->sh_info > num)
+	warn (_("Section %u has an out of range sh_info value of %u\n"), i, internal->sh_info);
     }
 
   free (shdrs);
@@ -5117,6 +5121,10 @@ get_64bit_section_headers (FILE * file, bfd_boolean probe)
       internal->sh_info      = BYTE_GET (shdrs[i].sh_info);
       internal->sh_offset    = BYTE_GET (shdrs[i].sh_offset);
       internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
+      if (!probe && internal->sh_link > num)
+	warn (_("Section %u has an out of range sh_link value of %u\n"), i, internal->sh_link);
+      if (!probe && internal->sh_flags & SHF_INFO_LINK && internal->sh_info > num)
+	warn (_("Section %u has an out of range sh_info value of %u\n"), i, internal->sh_info);
     }
 
   free (shdrs);
-- 
GitLab