PR22047, Heap out of bounds read in parse_comp_unit
[deliverable/binutils-gdb.git] / bfd / dwarf2.c
index 8abb3f0b37ac70c02d7f4ac9652b359b483e1346..4cf50ccddd07d08f4961c521c6efb26cfd64fd35 100644 (file)
@@ -125,8 +125,7 @@ struct dwarf2_debug
 
   /* A pointer to the memory block allocated for info_ptr.  Neither
      info_ptr nor sec_info_ptr are guaranteed to stay pointing to the
-     beginning of the malloc block.  This is used only to free the
-     memory later.  */
+     beginning of the malloc block.  */
   bfd_byte *info_ptr_memory;
 
   /* Pointer to the symbol table.  */
@@ -250,9 +249,6 @@ struct comp_unit
      by its reference.  */
   bfd_byte *info_ptr_unit;
 
-  /* Pointer to the start of the debug section, for DW_FORM_ref_addr.  */
-  bfd_byte *sec_info_ptr;
-
   /* The offset into .debug_line of the line number table.  */
   unsigned long line_offset;
 
@@ -530,9 +526,10 @@ read_section (bfd *           abfd,
 {
   asection *msec;
   const char *section_name = sec->uncompressed_name;
+  bfd_byte *contents = *section_buffer;
 
   /* The section may have already been read.  */
-  if (*section_buffer == NULL)
+  if (contents == NULL)
     {
       msec = bfd_get_section_by_name (abfd, section_name);
       if (! msec)
@@ -550,45 +547,21 @@ read_section (bfd *           abfd,
        }
 
       *section_size = msec->rawsize ? msec->rawsize : msec->size;
-      if (syms)
-       {
-         *section_buffer
-           = bfd_simple_get_relocated_section_contents (abfd, msec, NULL, syms);
-         if (! *section_buffer)
-           return FALSE;
-       }
-      else
-       {
-         *section_buffer = (bfd_byte *) bfd_malloc (*section_size);
-         if (! *section_buffer)
-           return FALSE;
-         if (! bfd_get_section_contents (abfd, msec, *section_buffer,
-                                         0, *section_size))
-           return FALSE;
-       }
-
-      /* Paranoia - if we are reading in a string section, make sure that it
-        is NUL terminated.  This is to prevent string functions from running
-        off the end of the buffer.  Note - knowing the size of the buffer is
-        not enough as some functions, eg strchr, do not have a range limited
-        equivalent.
-
-        FIXME: We ought to use a flag in the dwarf_debug_sections[] table to
-        determine the nature of a debug section, rather than checking the
-        section name as we do here.  */
-      if (*section_size > 0
-         && (*section_buffer)[*section_size - 1] != 0
-         && (strstr (section_name, "_str") || strstr (section_name, "names")))
+      /* Paranoia - alloc one extra so that we can make sure a string
+        section is NUL terminated.  */
+      contents = (bfd_byte *) bfd_malloc (*section_size + 1);
+      if (contents == NULL)
+       return FALSE;
+      if (syms
+         ? !bfd_simple_get_relocated_section_contents (abfd, msec, contents,
+                                                       syms)
+         : !bfd_get_section_contents (abfd, msec, contents, 0, *section_size))
        {
-         bfd_byte * new_buffer = malloc (*section_size + 1);
-
-         _bfd_error_handler (_("warning: dwarf string section '%s' is not NUL terminated"),
-                             section_name);
-         memcpy (new_buffer, *section_buffer, *section_size);
-         new_buffer[*section_size] = 0;
-         free (*section_buffer);
-         *section_buffer = new_buffer;
+         free (contents);
+         return FALSE;
        }
+      contents[*section_size] = 0;
+      *section_buffer = contents;
     }
 
   /* It is possible to get a bad value for the offset into the section
@@ -1597,6 +1570,8 @@ concat_filename (struct line_info_table *table, unsigned int file)
     }
 
   filename = table->files[file - 1].name;
+  if (filename == NULL)
+    return strdup ("<unknown>");
 
   if (!IS_ABSOLUTE_PATH (filename))
     {
@@ -1940,17 +1915,29 @@ read_formatted_entries (struct comp_unit *unit, bfd_byte **bufp,
       return FALSE;
     }
 
+  /* PR 22210.  Paranoia check.  Don't bother running the loop
+     if we know that we are going to run out of buffer.  */
+  if (data_count > (bfd_vma) (buf_end - buf))
+    {
+      _bfd_error_handler (_("Dwarf Error: data count (%Lx) larger than buffer size."),
+                         data_count);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
   for (datai = 0; datai < data_count; datai++)
     {
       bfd_byte *format = format_header_data;
       struct fileinfo fe;
 
+      memset (&fe, 0, sizeof fe);
       for (formati = 0; formati < format_count; formati++)
        {
          bfd_vma content_type, form;
          char *string_trash;
          char **stringp = &string_trash;
          unsigned int uint_trash, *uintp = &uint_trash;
+         struct attribute attr;
 
          content_type = _bfd_safe_read_leb128 (abfd, format, &bytes_read,
                                                FALSE, buf_end);
@@ -1982,47 +1969,23 @@ read_formatted_entries (struct comp_unit *unit, bfd_byte **bufp,
          form = _bfd_safe_read_leb128 (abfd, format, &bytes_read, FALSE,
                                        buf_end);
          format += bytes_read;
+
+         buf = read_attribute_value (&attr, form, 0, unit, buf, buf_end);
+         if (buf == NULL)
+           return FALSE;
          switch (form)
            {
            case DW_FORM_string:
-             *stringp = read_string (abfd, buf, buf_end, &bytes_read);
-             buf += bytes_read;
-             break;
-
            case DW_FORM_line_strp:
-             *stringp = read_indirect_line_string (unit, buf, buf_end, &bytes_read);
-             buf += bytes_read;
+             *stringp = attr.u.str;
              break;
 
            case DW_FORM_data1:
-             *uintp = read_1_byte (abfd, buf, buf_end);
-             buf += 1;
-             break;
-
            case DW_FORM_data2:
-             *uintp = read_2_bytes (abfd, buf, buf_end);
-             buf += 2;
-             break;
-
            case DW_FORM_data4:
-             *uintp = read_4_bytes (abfd, buf, buf_end);
-             buf += 4;
-             break;
-
            case DW_FORM_data8:
-             *uintp = read_8_bytes (abfd, buf, buf_end);
-             buf += 8;
-             break;
-
            case DW_FORM_udata:
-             *uintp = _bfd_safe_read_leb128 (abfd, buf, &bytes_read, FALSE,
-                                             buf_end);
-             buf += bytes_read;
-             break;
-
-           case DW_FORM_block:
-             /* It is valid only for DW_LNCT_timestamp which is ignored by
-                current GDB.  */
+             *uintp = attr.u.val;
              break;
            }
        }
@@ -2268,6 +2231,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash)
       unsigned int discriminator = 0;
       int is_stmt = lh.default_is_stmt;
       int end_sequence = 0;
+      unsigned int dir, xtime, size;
       /* eraxxon@alumni.rice.edu: Against the DWARF2 specs, some
         compilers generate address sequences that are wildly out of
         order using DW_LNE_set_address (e.g. Intel C++ 6.0 compiler
@@ -2277,7 +2241,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash)
       bfd_vma high_pc = 0;
 
       /* Decode the table.  */
-      while (! end_sequence)
+      while (!end_sequence && line_ptr < line_end)
        {
          op_code = read_1_byte (abfd, line_ptr, line_end);
          line_ptr += 1;
@@ -2342,31 +2306,18 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash)
                case DW_LNE_define_file:
                  cur_file = read_string (abfd, line_ptr, line_end, &bytes_read);
                  line_ptr += bytes_read;
-                 if ((table->num_files % FILE_ALLOC_CHUNK) == 0)
-                   {
-                     struct fileinfo *tmp;
-
-                     amt = table->num_files + FILE_ALLOC_CHUNK;
-                     amt *= sizeof (struct fileinfo);
-                     tmp = (struct fileinfo *) bfd_realloc (table->files, amt);
-                     if (tmp == NULL)
-                       goto line_fail;
-                     table->files = tmp;
-                   }
-                 table->files[table->num_files].name = cur_file;
-                 table->files[table->num_files].dir =
-                   _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
-                                          FALSE, line_end);
+                 dir = _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
+                                              FALSE, line_end);
                  line_ptr += bytes_read;
-                 table->files[table->num_files].time =
-                   _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
-                                          FALSE, line_end);
+                 xtime = _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
+                                                FALSE, line_end);
                  line_ptr += bytes_read;
-                 table->files[table->num_files].size =
-                   _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
-                                          FALSE, line_end);
+                 size = _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
+                                               FALSE, line_end);
                  line_ptr += bytes_read;
-                 table->num_files++;
+                 if (!line_info_add_file_name (table, cur_file, dir,
+                                               xtime, size))
+                   goto line_fail;
                  break;
                case DW_LNE_set_discriminator:
                  discriminator =
@@ -2858,21 +2809,37 @@ find_abstract_instance_name (struct comp_unit *unit,
   if (attr_ptr->form == DW_FORM_ref_addr)
     {
       /* We only support DW_FORM_ref_addr within the same file, so
-        any relocations should be resolved already.  */
-      if (!die_ref)
+        any relocations should be resolved already.  Check this by
+        testing for a zero die_ref;  There can't be a valid reference
+        to the header of a .debug_info section.
+        DW_FORM_ref_addr is an offset relative to .debug_info.
+        Normally when using the GNU linker this is accomplished by
+        emitting a symbolic reference to a label, because .debug_info
+        sections are linked at zero.  When there are multiple section
+        groups containing .debug_info, as there might be in a
+        relocatable object file, it would be reasonable to assume that
+        a symbolic reference to a label in any .debug_info section
+        might be used.  Since we lay out multiple .debug_info
+        sections at non-zero VMAs (see place_sections), and read
+        them contiguously into stash->info_ptr_memory, that means
+        the reference is relative to stash->info_ptr_memory.  */
+      size_t total;
+
+      info_ptr = unit->stash->info_ptr_memory;
+      info_ptr_end = unit->stash->info_ptr_end;
+      total = info_ptr_end - info_ptr;
+      if (!die_ref || die_ref >= total)
        {
          _bfd_error_handler
-           (_("Dwarf Error: Abstract instance DIE ref zero."));
+           (_("Dwarf Error: Invalid abstract instance DIE ref."));
          bfd_set_error (bfd_error_bad_value);
          return FALSE;
        }
-
-      info_ptr = unit->sec_info_ptr + die_ref;
-      info_ptr_end = unit->end_ptr;
+      info_ptr += die_ref;
 
       /* Now find the CU containing this pointer.  */
       if (info_ptr >= unit->info_ptr_unit && info_ptr < unit->end_ptr)
-       ;
+       info_ptr_end = unit->end_ptr;
       else
        {
          /* Check other CUs to see if they contain the abbrev.  */
@@ -2888,7 +2855,10 @@ find_abstract_instance_name (struct comp_unit *unit,
                break;
 
          if (u)
-           unit = u;
+           {
+             unit = u;
+             info_ptr_end = unit->end_ptr;
+           }
          /* else FIXME: What do we do now ?  */
        }
     }
@@ -2911,8 +2881,22 @@ find_abstract_instance_name (struct comp_unit *unit,
     }
   else
     {
-      info_ptr = unit->info_ptr_unit + die_ref;
+      /* DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8 or
+        DW_FORM_ref_udata.  These are all references relative to the
+        start of the current CU.  */
+      size_t total;
+
+      info_ptr = unit->info_ptr_unit;
       info_ptr_end = unit->end_ptr;
+      total = info_ptr_end - info_ptr;
+      if (!die_ref || die_ref >= total)
+       {
+         _bfd_error_handler
+           (_("Dwarf Error: Invalid abstract instance DIE ref."));
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+      info_ptr += die_ref;
     }
 
   abbrev_number = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
@@ -3225,7 +3209,8 @@ scan_unit_for_symbols (struct comp_unit *unit)
              switch (attr.name)
                {
                case DW_AT_name:
-                 var->name = attr.u.str;
+                 if (is_str_attr (attr.form))
+                   var->name = attr.u.str;
                  break;
 
                case DW_AT_decl_file:
@@ -3459,7 +3444,6 @@ parse_comp_unit (struct dwarf2_debug *stash,
   unit->end_ptr = end_ptr;
   unit->stash = stash;
   unit->info_ptr_unit = info_ptr_unit;
-  unit->sec_info_ptr = stash->sec_info_ptr;
 
   for (i = 0; i < abbrev->num_attrs; ++i)
     {
@@ -3477,7 +3461,8 @@ parse_comp_unit (struct dwarf2_debug *stash,
          break;
 
        case DW_AT_name:
-         unit->name = attr.u.str;
+         if (is_str_attr (attr.form))
+           unit->name = attr.u.str;
          break;
 
        case DW_AT_low_pc:
This page took 0.039143 seconds and 4 git commands to generate.