[binutils-gdb] Sanity check reloc count in get_reloc_upper_bound

Alan Modra amodra@sourceware.org
Thu Nov 10 10:16:54 GMT 2022


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

commit a3eb71adfea201f9e14980d8a96ea2e27ef52ad1
Author: Alan Modra <amodra@gmail.com>
Date:   Thu Nov 10 11:48:01 2022 +1030

    Sanity check reloc count in get_reloc_upper_bound
    
    The idea here is the stop tools from allocating up to 32G per section
    for the arelent pointer array, only to find a little later that the
    section reloc count was fuzzed.  This usually doesn't hurt much (on
    systems that allow malloc overcommit) except when compiled with asan.
    
    We already do this for ELF targets, and while fixing the logic
    recently I decided other targets ought to do the same.
    
            * elf64-sparc.c (elf64_sparc_get_reloc_upper_bound): Sanity check
            section reloc count against file size.
            * mach-o.c (bfd_mach_o_get_reloc_upper_bound): Likewise.
            * aoutx.h (get_reloc_upper_bound): Likewise, and don't duplicate
            check done in bfd_get_reloc_upper_bound.
            * pdp11.c (get_reloc_upper_bound): Likewise.
            * coffgen.c (coff_get_reloc_upper_bound): Likewise.

Diff:
---
 bfd/aoutx.h       | 20 ++++++++++++--------
 bfd/coffgen.c     | 22 ++++++++++++++--------
 bfd/elf64-sparc.c | 19 +++++++++++++++----
 bfd/mach-o.c      | 22 ++++++++++++++++------
 bfd/pdp11.c       | 20 ++++++++++++--------
 5 files changed, 69 insertions(+), 34 deletions(-)

diff --git a/bfd/aoutx.h b/bfd/aoutx.h
index 4aed23426ca..61ea9f7ce04 100644
--- a/bfd/aoutx.h
+++ b/bfd/aoutx.h
@@ -2485,13 +2485,7 @@ NAME (aout, canonicalize_reloc) (bfd *abfd,
 long
 NAME (aout, get_reloc_upper_bound) (bfd *abfd, sec_ptr asect)
 {
-  bfd_size_type count;
-
-  if (bfd_get_format (abfd) != bfd_object)
-    {
-      bfd_set_error (bfd_error_invalid_operation);
-      return -1;
-    }
+  size_t count, raw;
 
   if (asect->flags & SEC_CONSTRUCTOR)
     count = asect->reloc_count;
@@ -2507,11 +2501,21 @@ NAME (aout, get_reloc_upper_bound) (bfd *abfd, sec_ptr asect)
       return -1;
     }
 
-  if (count >= LONG_MAX / sizeof (arelent *))
+  if (count >= LONG_MAX / sizeof (arelent *)
+      || _bfd_mul_overflow (count, obj_reloc_entry_size (abfd), &raw))
     {
       bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
+  if (!bfd_write_p (abfd))
+    {
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && raw > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return -1;
+	}
+    }
   return (count + 1) * sizeof (arelent *);
 }
 

diff --git a/bfd/coffgen.c b/bfd/coffgen.c
index 3e0fbc6184f..aab41c34ec7 100644
--- a/bfd/coffgen.c
+++ b/bfd/coffgen.c
@@ -1976,19 +1976,25 @@ coff_get_normalized_symtab (bfd *abfd)
 long
 coff_get_reloc_upper_bound (bfd *abfd, sec_ptr asect)
 {
-  if (bfd_get_format (abfd) != bfd_object)
+  size_t count, raw;
+
+  count = asect->reloc_count;
+  if (count >= LONG_MAX / sizeof (arelent *)
+      || _bfd_mul_overflow (count, bfd_coff_relsz (abfd), &raw))
     {
-      bfd_set_error (bfd_error_invalid_operation);
+      bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
-#if SIZEOF_LONG == SIZEOF_INT
-  if (asect->reloc_count >= LONG_MAX / sizeof (arelent *))
+  if (!bfd_write_p (abfd))
     {
-      bfd_set_error (bfd_error_file_too_big);
-      return -1;
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && raw > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return -1;
+	}
     }
-#endif
-  return (asect->reloc_count + 1L) * sizeof (arelent *);
+  return (count + 1) * sizeof (arelent *);
 }
 
 asymbol *
diff --git a/bfd/elf64-sparc.c b/bfd/elf64-sparc.c
index e9f03cf8e3a..fb4483dcd17 100644
--- a/bfd/elf64-sparc.c
+++ b/bfd/elf64-sparc.c
@@ -37,14 +37,25 @@
 static long
 elf64_sparc_get_reloc_upper_bound (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
 {
-#if SIZEOF_LONG == SIZEOF_INT
-  if (sec->reloc_count >= LONG_MAX / 2 / sizeof (arelent *))
+  size_t count, raw;
+
+  count = sec->reloc_count;
+  if (count >= LONG_MAX / 2 / sizeof (arelent *)
+      || _bfd_mul_overflow (count, sizeof (Elf64_External_Rela), &raw))
     {
       bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
-#endif
-  return (sec->reloc_count * 2L + 1) * sizeof (arelent *);
+  if (!bfd_write_p (abfd))
+    {
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && raw > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return -1;
+	}
+    }
+  return (count * 2 + 1) * sizeof (arelent *);
 }
 
 static long
diff --git a/bfd/mach-o.c b/bfd/mach-o.c
index 5279343768c..664ff44a8e7 100644
--- a/bfd/mach-o.c
+++ b/bfd/mach-o.c
@@ -1407,17 +1407,27 @@ bfd_mach_o_write_dyld_info (bfd *abfd, bfd_mach_o_load_command *command)
 }
 
 long
-bfd_mach_o_get_reloc_upper_bound (bfd *abfd ATTRIBUTE_UNUSED,
-				  asection *asect)
+bfd_mach_o_get_reloc_upper_bound (bfd *abfd, asection *asect)
 {
-#if SIZEOF_LONG == SIZEOF_INT
-   if (asect->reloc_count >= LONG_MAX / sizeof (arelent *))
+  size_t count, raw;
+
+  count = asect->reloc_count;
+  if (count >= LONG_MAX / sizeof (arelent *)
+      || _bfd_mul_overflow (count, BFD_MACH_O_RELENT_SIZE, &raw))
     {
       bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
-#endif
- return (asect->reloc_count + 1L) * sizeof (arelent *);
+  if (!bfd_write_p (abfd))
+    {
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && raw > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return -1;
+	}
+    }
+  return (count + 1) * sizeof (arelent *);
 }
 
 /* In addition to the need to byte-swap the symbol number, the bit positions
diff --git a/bfd/pdp11.c b/bfd/pdp11.c
index 9ef63cc311c..de9c8690e20 100644
--- a/bfd/pdp11.c
+++ b/bfd/pdp11.c
@@ -2125,13 +2125,7 @@ NAME (aout, canonicalize_reloc) (bfd *abfd,
 long
 NAME (aout, get_reloc_upper_bound) (bfd *abfd, sec_ptr asect)
 {
-  bfd_size_type count;
-
-  if (bfd_get_format (abfd) != bfd_object)
-    {
-      bfd_set_error (bfd_error_invalid_operation);
-      return -1;
-    }
+  size_t count, raw;
 
   if (asect->flags & SEC_CONSTRUCTOR)
     count = asect->reloc_count;
@@ -2147,11 +2141,21 @@ NAME (aout, get_reloc_upper_bound) (bfd *abfd, sec_ptr asect)
       return -1;
     }
 
-  if (count >= LONG_MAX / sizeof (arelent *))
+  if (count >= LONG_MAX / sizeof (arelent *)
+      || _bfd_mul_overflow (count, obj_reloc_entry_size (abfd), &raw))
     {
       bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
+  if (!bfd_write_p (abfd))
+    {
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && raw > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return -1;
+	}
+    }
   return (count + 1) * sizeof (arelent *);
 }


More information about the Binutils-cvs mailing list