Resetting section vma after _bfd_dwarf2_find_nearest_line
authorAlan Modra <amodra@gmail.com>
Sat, 4 Feb 2023 00:59:05 +0000 (11:29 +1030)
committerAlan Modra <amodra@gmail.com>
Mon, 6 Feb 2023 00:01:26 +0000 (10:31 +1030)
There are failure paths in _bfd_dwarf2_slurp_debug_info that can
result in altered section vmas.  Also, when setting ET_REL section
vmas it's not too difficult to handle cases where the original vma was
non-zero, so do that too.

This patch was really in response to an addr2line buffer overflow
processing a fuzzed mips relocatable object file.  The file had a
number of .debug_info sections with relocations that included lo16 and
hi16 relocs, and in that order.  At least one section VMA was
non-zero.  This resulted in processing of DWARF info twice, once via
the call to _bfd_dwarf2_find_nearest_line in
_bfd_mips_elf_find_nearest_line, and because that failed leaving VMAs
altered, the second via the call in _bfd_elf_find_nearest_line.  The
first call left entries on mips_hi16_list pointing at buffers
allocated during the first call, the second call processed the
mips_hi16_list after the buffers had been freed.  (At least when
running with asan and under valgrind.  Under gdb with a non-asan
addr2line the second call allocated exactly the same buffer and the
bug didn't show.)  Now I don't really care too much what happens with
fuzzed files, but the logic in _bfd_dwarf2_find_nearest_line is meant
to result in only one read of .debug_info, not multiple reads of the
same info when there are errors.  This patch fixes that problem.

* dwarf2.c (struct adjusted_section): Add orig_vma.
(unset_sections): Reset vma to it.
(place_sections): Handle non-zero vma too.  Save orig_vma.
(_bfd_dwarf2_slurp_debug_info): Tidy.  Correct outdated comment.
On error returns after calling place_sections, call
unset_sections.
(_bfd_dwarf2_find_nearest_line_with_alt): Simplify call to
unset_sections.

bfd/dwarf2.c

index 835851e6afc7eb75d2d0ab2387ce95c5b22c98bf..ab5a9f37ec194e2fe941013266c2d0049c579a24 100644 (file)
@@ -82,6 +82,7 @@ struct adjusted_section
 {
   asection *section;
   bfd_vma adj_vma;
+  bfd_vma orig_vma;
 };
 
 /* A trie to map quickly from address range to compilation unit.
@@ -4952,7 +4953,7 @@ unset_sections (struct dwarf2_debug *stash)
   i = stash->adjusted_section_count;
   p = stash->adjusted_sections;
   for (; i > 0; i--, p++)
-    p->section->vma = 0;
+    p->section->vma = p->orig_vma;
 }
 
 /* Set VMAs for allocated and .debug_info sections in ORIG_BFD, a
@@ -4993,10 +4994,9 @@ place_sections (bfd *orig_bfd, struct dwarf2_debug *stash)
        {
          int is_debug_info;
 
-         if ((sect->output_section != NULL
-              && sect->output_section != sect
-              && (sect->flags & SEC_DEBUGGING) == 0)
-             || sect->vma != 0)
+         if (sect->output_section != NULL
+             && sect->output_section != sect
+             && (sect->flags & SEC_DEBUGGING) == 0)
            continue;
 
          is_debug_info = (strcmp (sect->name, debug_info_name) == 0
@@ -5037,10 +5037,9 @@ place_sections (bfd *orig_bfd, struct dwarf2_debug *stash)
              bfd_size_type sz;
              int is_debug_info;
 
-             if ((sect->output_section != NULL
-                  && sect->output_section != sect
-                  && (sect->flags & SEC_DEBUGGING) == 0)
-                 || sect->vma != 0)
+             if (sect->output_section != NULL
+                 && sect->output_section != sect
+                 && (sect->flags & SEC_DEBUGGING) == 0)
                continue;
 
              is_debug_info = (strcmp (sect->name, debug_info_name) == 0
@@ -5052,24 +5051,17 @@ place_sections (bfd *orig_bfd, struct dwarf2_debug *stash)
 
              sz = sect->rawsize ? sect->rawsize : sect->size;
 
-             if (is_debug_info)
-               {
-                 BFD_ASSERT (sect->alignment_power == 0);
-                 sect->vma = last_dwarf;
-                 last_dwarf += sz;
-               }
-             else
-               {
-                 /* Align the new address to the current section
-                    alignment.  */
-                 last_vma = ((last_vma
-                              + ~(-((bfd_vma) 1 << sect->alignment_power)))
-                             & (-((bfd_vma) 1 << sect->alignment_power)));
-                 sect->vma = last_vma;
-                 last_vma += sz;
-               }
-
              p->section = sect;
+             p->orig_vma = sect->vma;
+
+             bfd_vma *v = is_debug_info ? &last_dwarf : &last_vma;
+             /* Align the new address to the current section
+                alignment.  */
+             bfd_vma mask = -(bfd_vma) 1 << sect->alignment_power;
+             *v = (*v + ~mask) & mask;
+             sect->vma = *v;
+             *v += sz;
+
              p->adj_vma = sect->vma;
              p++;
            }
@@ -5379,7 +5371,6 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
                              void **pinfo,
                              bool do_place)
 {
-  size_t amt = sizeof (struct dwarf2_debug);
   bfd_size_type total_size;
   asection *msec;
   struct dwarf2_debug *stash = (struct dwarf2_debug *) *pinfo;
@@ -5401,11 +5392,11 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
          return false;
        }
       _bfd_dwarf2_cleanup_debug_info (abfd, pinfo);
-      memset (stash, 0, amt);
+      memset (stash, 0, sizeof (*stash));
     }
   else
     {
-      stash = (struct dwarf2_debug *) bfd_zalloc (abfd, amt);
+      stash = (struct dwarf2_debug *) bfd_zalloc (abfd, sizeof (*stash));
       if (! stash)
        return false;
       *pinfo = stash;
@@ -5482,14 +5473,11 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
 
   /* There can be more than one DWARF2 info section in a BFD these
      days.  First handle the easy case when there's only one.  If
-     there's more than one, try case two: none of the sections is
-     compressed.  In that case, read them all in and produce one
-     large stash.  We do this in two passes - in the first pass we
+     there's more than one, try case two: read them all in and produce
+     one large stash.  We do this in two passes - in the first pass we
      just accumulate the section sizes, and in the second pass we
      read in the section's contents.  (The allows us to avoid
-     reallocing the data as we add sections to the stash.)  If
-     some or all sections are compressed, then do things the slow
-     way, with a bunch of reallocs.  */
+     reallocing the data as we add sections to the stash.)  */
 
   if (! find_debug_info (debug_bfd, debug_sections, msec))
     {
@@ -5498,7 +5486,7 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
       if (! read_section (debug_bfd, &stash->debug_sections[debug_info],
                          symbols, 0,
                          &stash->f.dwarf_info_buffer, &total_size))
-       return false;
+       goto restore_vma;
     }
   else
     {
@@ -5508,19 +5496,19 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
           msec = find_debug_info (debug_bfd, debug_sections, msec))
        {
          if (_bfd_section_size_insane (debug_bfd, msec))
-           return false;
+           goto restore_vma;
          /* Catch PR25070 testcase overflowing size calculation here.  */
          if (total_size + msec->size < total_size)
            {
              bfd_set_error (bfd_error_no_memory);
-             return false;
+             goto restore_vma;
            }
          total_size += msec->size;
        }
 
       stash->f.dwarf_info_buffer = (bfd_byte *) bfd_malloc (total_size);
       if (stash->f.dwarf_info_buffer == NULL)
-       return false;
+       goto restore_vma;
 
       total_size = 0;
       for (msec = find_debug_info (debug_bfd, debug_sections, NULL);
@@ -5536,7 +5524,7 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
          if (!(bfd_simple_get_relocated_section_contents
                (debug_bfd, msec, stash->f.dwarf_info_buffer + total_size,
                 symbols)))
-           return false;
+           goto restore_vma;
 
          total_size += size;
        }
@@ -5545,6 +5533,10 @@ _bfd_dwarf2_slurp_debug_info (bfd *abfd, bfd *debug_bfd,
   stash->f.info_ptr = stash->f.dwarf_info_buffer;
   stash->f.dwarf_info_size = total_size;
   return true;
+
+ restore_vma:
+  unset_sections (stash);
+  return false;
 }
 
 /* Parse the next DWARF2 compilation unit at FILE->INFO_PTR.  */
@@ -6038,8 +6030,7 @@ _bfd_dwarf2_find_nearest_line_with_alt
        }
     }
 
-  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
-    unset_sections (stash);
+  unset_sections (stash);
 
   return found;
 }