More fixes for buffer overruns instigated by corrupt binaries.
[deliverable/binutils-gdb.git] / bfd / coffgen.c
index 584f64b57b53306e25f37fda5f742380ed0619ba..d0bf2c1a181a9ea6877063d4155ed07f563277fe 100644 (file)
@@ -1,7 +1,5 @@
 /* Support for the generic parts of COFF, for BFD.
-   Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   Copyright (C) 1990-2014 Free Software Foundation, Inc.
    Written by Cygnus Support.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -86,9 +84,8 @@ make_a_section_from_file (bfd *abfd,
          strings = _bfd_coff_read_string_table (abfd);
          if (strings == NULL)
            return FALSE;
-         /* FIXME: For extra safety, we should make sure that
-             strindex does not run us past the end, but right now we
-             don't know the length of the string table.  */
+         if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd))
+           return FALSE;
          strings += strindex;
          name = (char *) bfd_alloc (abfd,
                                      (bfd_size_type) strlen (strings) + 1 + 1);
@@ -221,8 +218,12 @@ make_a_section_from_file (bfd *abfd,
 
 /* Read in a COFF object and make it into a BFD.  This is used by
    ECOFF as well.  */
-
-static const bfd_target *
+const bfd_target *
+coff_real_object_p (bfd *,
+                    unsigned,
+                    struct internal_filehdr *,
+                    struct internal_aouthdr *);
+const bfd_target *
 coff_real_object_p (bfd *abfd,
                    unsigned nscns,
                    struct internal_filehdr *internal_f,
@@ -462,6 +463,8 @@ _bfd_coff_internal_syment_name (bfd *abfd,
          if (strings == NULL)
            return NULL;
        }
+      if (sym->_n._n_n._n_offset >= obj_coff_strings_len (abfd))
+       return NULL;
       return strings + sym->_n._n_n._n_offset;
     }
 }
@@ -647,7 +650,7 @@ fixup_symbol_value (bfd *abfd,
                    struct internal_syment *syment)
 {
   /* Normalize the symbol flags.  */
-  if (coff_symbol_ptr->symbol.section 
+  if (coff_symbol_ptr->symbol.section
       && bfd_is_com_section (coff_symbol_ptr->symbol.section))
     {
       /* A common symbol is undefined with a value.  */
@@ -1519,7 +1522,7 @@ coff_pointerize_aux (bfd *abfd,
   /* Otherwise patch up.  */
 #define N_TMASK coff_data  (abfd)->local_n_tmask
 #define N_BTSHFT coff_data (abfd)->local_n_btshft
-  
+
   if ((ISFCN (type) || ISTAG (n_sclass) || n_sclass == C_BLOCK
        || n_sclass == C_FCN)
       && auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l > 0)
@@ -1543,7 +1546,7 @@ coff_pointerize_aux (bfd *abfd,
    we didn't want to go to the trouble until someone needed it.  */
 
 static char *
-build_debug_section (bfd *abfd)
+build_debug_section (bfd *abfd, asection ** sect_return)
 {
   char *debug_section;
   file_ptr position;
@@ -1571,6 +1574,8 @@ build_debug_section (bfd *abfd)
       || bfd_bread (debug_section, sec_size, abfd) != sec_size
       || bfd_seek (abfd, position, SEEK_SET) != 0)
     return NULL;
+
+  * sect_return = sect;
   return debug_section;
 }
 
@@ -1614,6 +1619,11 @@ _bfd_coff_get_external_symbols (bfd *abfd)
   if (size == 0)
     return TRUE;
 
+  /* PR binutils/17512: Do not even try to load
+     a symbol table bigger than the entire file...  */
+  if (size >= (bfd_size_type) bfd_get_size (abfd))
+    return FALSE;
+
   syms = bfd_malloc (size);
   if (syms == NULL)
     return FALSE;
@@ -1633,7 +1643,9 @@ _bfd_coff_get_external_symbols (bfd *abfd)
 
 /* Read in the external strings.  The strings are not loaded until
    they are needed.  This is because we have no simple way of
-   detecting a missing string table in an archive.  */
+   detecting a missing string table in an archive.  If the strings
+   are loaded then the STRINGS and STRINGS_LEN fields in the
+   coff_tdata structure will be set.  */
 
 const char *
 _bfd_coff_read_string_table (bfd *abfd)
@@ -1695,6 +1707,7 @@ _bfd_coff_read_string_table (bfd *abfd)
     }
 
   obj_coff_strings (abfd) = strings;
+  obj_coff_strings_len (abfd) = strsize;
 
   return strings;
 }
@@ -1715,6 +1728,7 @@ _bfd_coff_free_symbols (bfd *abfd)
     {
       free (obj_coff_strings (abfd));
       obj_coff_strings (abfd) = NULL;
+      obj_coff_strings_len (abfd) = 0;
     }
   return TRUE;
 }
@@ -1735,18 +1749,27 @@ coff_get_normalized_symtab (bfd *abfd)
   char *raw_src;
   char *raw_end;
   const char *string_table = NULL;
-  char *debug_section = NULL;
+  asection * debug_sec = NULL;
+  char *debug_sec_data = NULL;
   bfd_size_type size;
 
   if (obj_raw_syments (abfd) != NULL)
     return obj_raw_syments (abfd);
 
-  size = obj_raw_syment_count (abfd) * sizeof (combined_entry_type);
+  size = obj_raw_syment_count (abfd);
+  if (size == 0)
+    return NULL;
+  /* PR binutils/17512: Do not even try to load
+     a symbol table bigger than the entire file...  */
+  if (size >= (bfd_size_type) bfd_get_size (abfd))
+    return NULL;
+
+  size *= sizeof (combined_entry_type);
   internal = (combined_entry_type *) bfd_zalloc (abfd, size);
   if (internal == NULL && size != 0)
     return NULL;
   internal_end = internal + obj_raw_syment_count (abfd);
-
+  
   if (! _bfd_coff_get_external_symbols (abfd))
     return NULL;
 
@@ -1764,8 +1787,8 @@ coff_get_normalized_symtab (bfd *abfd)
        raw_src < raw_end;
        raw_src += symesz, internal_ptr++)
     {
-
       unsigned int i;
+
       bfd_coff_swap_sym_in (abfd, (void *) raw_src,
                            (void *) & internal_ptr->u.syment);
       symbol_ptr = internal_ptr;
@@ -1775,6 +1798,10 @@ coff_get_normalized_symtab (bfd *abfd)
           i++)
        {
          internal_ptr++;
+         /* PR 17512: Prevent buffer overrun.  */
+         if (internal_ptr >= internal_end)
+           return NULL;
+
          raw_src += symesz;
          bfd_coff_swap_aux_in (abfd, (void *) raw_src,
                                symbol_ptr->u.syment.n_type,
@@ -1808,11 +1835,14 @@ coff_get_normalized_symtab (bfd *abfd)
                  if (string_table == NULL)
                    return NULL;
                }
-
-             internal_ptr->u.syment._n._n_n._n_offset =
-               ((bfd_hostptr_t)
-                (string_table
-                 + (internal_ptr + 1)->u.auxent.x_file.x_n.x_offset));
+             if ((bfd_size_type)((internal_ptr + 1)->u.auxent.x_file.x_n.x_offset)
+                 >= obj_coff_strings_len (abfd))
+               internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t) _("<corrupt>");
+             else
+               internal_ptr->u.syment._n._n_n._n_offset =
+                 ((bfd_hostptr_t)
+                  (string_table
+                   + (internal_ptr + 1)->u.auxent.x_file.x_n.x_offset));
            }
          else
            {
@@ -1867,18 +1897,31 @@ coff_get_normalized_symtab (bfd *abfd)
                  if (string_table == NULL)
                    return NULL;
                }
-             internal_ptr->u.syment._n._n_n._n_offset =
-               ((bfd_hostptr_t)
-                (string_table
-                 + internal_ptr->u.syment._n._n_n._n_offset));
+             if (internal_ptr->u.syment._n._n_n._n_offset >= obj_coff_strings_len (abfd))
+               internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t) _("<corrupt>");
+             else
+               internal_ptr->u.syment._n._n_n._n_offset =
+                 ((bfd_hostptr_t)
+                  (string_table
+                   + internal_ptr->u.syment._n._n_n._n_offset));
            }
          else
            {
              /* Long name in debug section.  Very similar.  */
-             if (debug_section == NULL)
-               debug_section = build_debug_section (abfd);
-             internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t)
-               (debug_section + internal_ptr->u.syment._n._n_n._n_offset);
+             if (debug_sec_data == NULL)
+               debug_sec_data = build_debug_section (abfd, & debug_sec);
+             if (debug_sec_data != NULL)
+               {
+                 BFD_ASSERT (debug_sec != NULL);
+                 /* PR binutils/17512: Catch out of range offsets into the debug data.  */
+                 if (internal_ptr->u.syment._n._n_n._n_offset > debug_sec->size)
+                   internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t) _("<corrupt>");
+                 else
+                   internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t)
+                     (debug_sec_data + internal_ptr->u.syment._n._n_n._n_offset);
+               }
+             else
+               internal_ptr->u.syment._n._n_n._n_offset = (bfd_hostptr_t) "";
            }
        }
       internal_ptr += internal_ptr->u.syment.n_numaux;
@@ -1942,7 +1985,7 @@ coff_bfd_make_debug_symbol (bfd *abfd,
   new_symbol->lineno = NULL;
   new_symbol->done_lineno = FALSE;
   new_symbol->symbol.the_bfd = abfd;
-  
+
   return & new_symbol->symbol;
 }
 
@@ -2189,13 +2232,13 @@ _bfd_coff_is_local_label_name (bfd *abfd ATTRIBUTE_UNUSED,
 
 bfd_boolean
 coff_find_nearest_line_with_names (bfd *abfd,
-                                   const struct dwarf_debug_section *debug_sections,
-                                   asection *section,
                                    asymbol **symbols,
+                                   asection *section,
                                    bfd_vma offset,
                                    const char **filename_ptr,
                                    const char **functionname_ptr,
-                                   unsigned int *line_ptr)
+                                   unsigned int *line_ptr,
+                                   const struct dwarf_debug_section *debug_sections)
 {
   bfd_boolean found;
   unsigned int i;
@@ -2220,10 +2263,9 @@ coff_find_nearest_line_with_names (bfd *abfd,
     return TRUE;
 
   /* Also try examining DWARF2 debugging information.  */
-  if (_bfd_dwarf2_find_nearest_line (abfd, debug_sections,
-                                     section, symbols, offset,
+  if (_bfd_dwarf2_find_nearest_line (abfd, symbols, NULL, section, offset,
                                     filename_ptr, functionname_ptr,
-                                    line_ptr, NULL, 0,
+                                    line_ptr, NULL, debug_sections, 0,
                                     &coff_data(abfd)->dwarf2_find_line_info))
     return TRUE;
 
@@ -2405,37 +2447,21 @@ coff_find_nearest_line_with_names (bfd *abfd,
 
 bfd_boolean
 coff_find_nearest_line (bfd *abfd,
-                       asection *section,
                        asymbol **symbols,
+                       asection *section,
                        bfd_vma offset,
                        const char **filename_ptr,
                        const char **functionname_ptr,
-                       unsigned int *line_ptr)
-{
-  return coff_find_nearest_line_with_names (abfd, dwarf_debug_sections,
-                                            section, symbols, offset,
-                                            filename_ptr, functionname_ptr,
-                                            line_ptr);
-}
-
-bfd_boolean
-coff_find_nearest_line_discriminator (bfd *abfd,
-                                     asection *section,
-                                     asymbol **symbols,
-                                     bfd_vma offset,
-                                     const char **filename_ptr,
-                                     const char **functionname_ptr,
-                                     unsigned int *line_ptr,
-                                     unsigned int *discriminator)
+                       unsigned int *line_ptr,
+                       unsigned int *discriminator_ptr)
 {
-  *discriminator = 0;
-  return coff_find_nearest_line_with_names (abfd, dwarf_debug_sections,
-                                            section, symbols, offset,
+  if (discriminator_ptr)
+    *discriminator_ptr = 0;
+  return coff_find_nearest_line_with_names (abfd, symbols, section, offset,
                                             filename_ptr, functionname_ptr,
-                                            line_ptr);
+                                            line_ptr, dwarf_debug_sections);
 }
 
-
 bfd_boolean
 coff_find_inliner_info (bfd *abfd,
                        const char **filename_ptr,
This page took 0.031367 seconds and 4 git commands to generate.