display_debug_frames
authorAlan Modra <amodra@gmail.com>
Sat, 15 May 2021 05:54:03 +0000 (15:24 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 15 May 2021 05:59:36 +0000 (15:29 +0930)
* dwarf.c (display_debug_frames): Delete initial_length_size.
Avoid pointer UB.  Constrain data reads to length given in header.
Sanity check cie header length.  Only skip up to next FDE on
finding augmentation data too long.

binutils/ChangeLog
binutils/dwarf.c

index f2d363e7af21fc2616569f06c8a56aaa5968465e..29d4406c61f0dc0bacd2a89b95b642e8877ae1da 100644 (file)
@@ -1,3 +1,10 @@
+2021-05-15  Alan Modra  <amodra@gmail.com>
+
+       * dwarf.c (display_debug_frames): Delete initial_length_size.
+       Avoid pointer UB.  Constrain data reads to length given in header.
+       Sanity check cie header length.  Only skip up to next FDE on
+       finding augmentation data too long.
+
 2021-05-15  Alan Modra  <amodra@gmail.com>
 
        * dwarf.c (read_cie): Add more sanity checks to ensure data
index 93e6d7319fa070a5e550cc228d2f3c2e25b62260..d2af05acb7ced5b4ef3a56678cd5aae54a0818d0 100644 (file)
@@ -8587,7 +8587,6 @@ display_debug_frames (struct dwarf_section *section,
       bfd_size_type augmentation_data_len = 0;
       unsigned int encoded_ptr_size = saved_eh_addr_size;
       unsigned int offset_size;
-      unsigned int initial_length_size;
       bool all_nops;
       static Frame_Chunk fde_fc;
 
@@ -8612,24 +8611,21 @@ display_debug_frames (struct dwarf_section *section,
        {
          SAFE_BYTE_GET_AND_INC (length, start, 8, end);
          offset_size = 8;
-         initial_length_size = 12;
        }
       else
-       {
-         offset_size = 4;
-         initial_length_size = 4;
-       }
+       offset_size = 4;
 
-      block_end = saved_start + length + initial_length_size;
-      if (block_end > end || block_end < start)
+      if (length > (size_t) (end - start))
        {
          warn ("Invalid length 0x%s in FDE at %#08lx\n",
                dwarf_vmatoa_1 (NULL, length, offset_size),
                (unsigned long) (saved_start - section_start));
          block_end = end;
        }
+      else
+       block_end = start + length;
 
-      SAFE_BYTE_GET_AND_INC (cie_id, start, offset_size, end);
+      SAFE_BYTE_GET_AND_INC (cie_id, start, offset_size, block_end);
 
       if (is_eh ? (cie_id == 0) : ((offset_size == 4 && cie_id == DW_CIE_ID)
                                   || (offset_size == 8 && cie_id == DW64_CIE_ID)))
@@ -8637,7 +8633,7 @@ display_debug_frames (struct dwarf_section *section,
          int version;
          unsigned int mreg;
 
-         start = read_cie (start, end, &cie, &version,
+         start = read_cie (start, block_end, &cie, &version,
                            &augmentation_data_len, &augmentation_data);
          /* PR 17512: file: 027-135133-0.005.  */
          if (cie == NULL)
@@ -8725,11 +8721,13 @@ display_debug_frames (struct dwarf_section *section,
                      SAFE_BYTE_GET_AND_INC (length, cie_scan, 8, end);
                      off_size = 8;
                    }
-                 if (length != 0)
+                 if (length != 0 && length <= (size_t) (end - cie_scan))
                    {
                      dwarf_vma c_id;
+                     unsigned char *cie_end = cie_scan + length;
 
-                     SAFE_BYTE_GET_AND_INC (c_id, cie_scan, off_size, end);
+                     SAFE_BYTE_GET_AND_INC (c_id, cie_scan, off_size,
+                                            cie_end);
                      if (is_eh
                          ? c_id == 0
                          : ((off_size == 4 && c_id == DW_CIE_ID)
@@ -8738,7 +8736,7 @@ display_debug_frames (struct dwarf_section *section,
                          int version;
                          unsigned int mreg;
 
-                         read_cie (cie_scan, end, &cie, &version,
+                         read_cie (cie_scan, cie_end, &cie, &version,
                                    &augmentation_data_len, &augmentation_data);
                          /* PR 17512: file: 3450-2098-0.004.  */
                          if (cie == NULL)
@@ -8823,29 +8821,32 @@ display_debug_frames (struct dwarf_section *section,
                  warn (_("Probably corrupt segment size: %d - using 4 instead\n"), fc->segment_size);
                  fc->segment_size = 4;
                }
-             SAFE_BYTE_GET_AND_INC (segment_selector, start, fc->segment_size, end);
+             SAFE_BYTE_GET_AND_INC (segment_selector, start,
+                                    fc->segment_size, block_end);
            }
 
-         fc->pc_begin = get_encoded_value (&start, fc->fde_encoding, section, end);
+         fc->pc_begin = get_encoded_value (&start, fc->fde_encoding, section,
+                                           block_end);
 
          /* FIXME: It appears that sometimes the final pc_range value is
             encoded in less than encoded_ptr_size bytes.  See the x86_64
             run of the "objcopy on compressed debug sections" test for an
             example of this.  */
-         SAFE_BYTE_GET_AND_INC (fc->pc_range, start, encoded_ptr_size, end);
+         SAFE_BYTE_GET_AND_INC (fc->pc_range, start, encoded_ptr_size,
+                                block_end);
 
          if (cie->augmentation[0] == 'z')
            {
-             READ_ULEB (augmentation_data_len, start, end);
+             READ_ULEB (augmentation_data_len, start, block_end);
              augmentation_data = start;
              /* PR 17512 file: 722-8446-0.004 and PR 22386.  */
-             if (augmentation_data_len > (bfd_size_type) (end - start))
+             if (augmentation_data_len > (bfd_size_type) (block_end - start))
                {
                  warn (_("Augmentation data too long: 0x%s, "
                          "expected at most %#lx\n"),
                        dwarf_vmatoa ("x", augmentation_data_len),
-                       (unsigned long) (end - start));
-                 start = end;
+                       (unsigned long) (block_end - start));
+                 start = block_end;
                  augmentation_data = NULL;
                  augmentation_data_len = 0;
                }
@@ -8889,7 +8890,6 @@ display_debug_frames (struct dwarf_section *section,
            {
              unsigned int reg, op, opa;
              unsigned long temp;
-             unsigned char * new_start;
 
              op = *start++;
              opa = op & 0x3f;
@@ -8903,7 +8903,7 @@ display_debug_frames (struct dwarf_section *section,
                case DW_CFA_advance_loc:
                  break;
                case DW_CFA_offset:
-                 SKIP_ULEB (start, end);
+                 SKIP_ULEB (start, block_end);
                  if (frame_need_space (fc, opa) >= 0)
                    fc->col_type[opa] = DW_CFA_undefined;
                  break;
@@ -8912,105 +8912,111 @@ display_debug_frames (struct dwarf_section *section,
                    fc->col_type[opa] = DW_CFA_undefined;
                  break;
                case DW_CFA_set_loc:
-                 start += encoded_ptr_size;
+                 if ((size_t) (block_end - start) < encoded_ptr_size)
+                   start = block_end;
+                 else
+                   start += encoded_ptr_size;
                  break;
                case DW_CFA_advance_loc1:
-                 start += 1;
+                 if ((size_t) (block_end - start) < 1)
+                   start = block_end;
+                 else
+                   start += 1;
                  break;
                case DW_CFA_advance_loc2:
-                 start += 2;
+                 if ((size_t) (block_end - start) < 2)
+                   start = block_end;
+                 else
+                   start += 2;
                  break;
                case DW_CFA_advance_loc4:
-                 start += 4;
+                 if ((size_t) (block_end - start) < 4)
+                   start = block_end;
+                 else
+                   start += 4;
                  break;
                case DW_CFA_offset_extended:
                case DW_CFA_val_offset:
-                 READ_ULEB (reg, start, end);
-                 SKIP_ULEB (start, end);
+                 READ_ULEB (reg, start, block_end);
+                 SKIP_ULEB (start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_restore_extended:
-                 READ_ULEB (reg, start, end);
+                 READ_ULEB (reg, start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_undefined:
-                 READ_ULEB (reg, start, end);
+                 READ_ULEB (reg, start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_same_value:
-                 READ_ULEB (reg, start, end);
+                 READ_ULEB (reg, start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_register:
-                 READ_ULEB (reg, start, end);
-                 SKIP_ULEB (start, end);
+                 READ_ULEB (reg, start, block_end);
+                 SKIP_ULEB (start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_def_cfa:
-                 SKIP_ULEB (start, end);
-                 SKIP_ULEB (start, end);
+                 SKIP_ULEB (start, block_end);
+                 SKIP_ULEB (start, block_end);
                  break;
                case DW_CFA_def_cfa_register:
-                 SKIP_ULEB (start, end);
+                 SKIP_ULEB (start, block_end);
                  break;
                case DW_CFA_def_cfa_offset:
-                 SKIP_ULEB (start, end);
+                 SKIP_ULEB (start, block_end);
                  break;
                case DW_CFA_def_cfa_expression:
-                 READ_ULEB (temp, start, end);
-                 new_start = start + temp;
-                 if (new_start < start)
-                   {
-                     warn (_("Corrupt CFA_def expression value: %lu\n"), temp);
-                     start = block_end;
-                   }
+                 READ_ULEB (temp, start, block_end);
+                 if ((size_t) (block_end - start) < temp)
+                   start = block_end;
                  else
-                   start = new_start;
+                   start += temp;
                  break;
                case DW_CFA_expression:
                case DW_CFA_val_expression:
-                 READ_ULEB (reg, start, end);
-                 READ_ULEB (temp, start, end);
-                 new_start = start + temp;
-                 if (new_start < start)
-                   {
-                     /* PR 17512: file:306-192417-0.005.  */
-                     warn (_("Corrupt CFA expression value: %lu\n"), temp);
-                     start = block_end;
-                   }
+                 READ_ULEB (reg, start, block_end);
+                 READ_ULEB (temp, start, block_end);
+                 if ((size_t) (block_end - start) < temp)
+                   start = block_end;
                  else
-                   start = new_start;
+                   start += temp;
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_offset_extended_sf:
                case DW_CFA_val_offset_sf:
-                 READ_ULEB (reg, start, end);
-                 SKIP_SLEB (start, end);
+                 READ_ULEB (reg, start, block_end);
+                 SKIP_SLEB (start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
                case DW_CFA_def_cfa_sf:
-                 SKIP_ULEB (start, end);
-                 SKIP_SLEB (start, end);
+                 SKIP_ULEB (start, block_end);
+                 SKIP_SLEB (start, block_end);
                  break;
                case DW_CFA_def_cfa_offset_sf:
-                 SKIP_SLEB (start, end);
+                 SKIP_SLEB (start, block_end);
                  break;
                case DW_CFA_MIPS_advance_loc8:
-                 start += 8;
+                 if ((size_t) (block_end - start) < 8)
+                   start = block_end;
+                 else
+                   start += 8;
                  break;
                case DW_CFA_GNU_args_size:
-                 SKIP_ULEB (start, end);
+                 SKIP_ULEB (start, block_end);
                  break;
                case DW_CFA_GNU_negative_offset_extended:
-                 READ_ULEB (reg, start, end);
-                 SKIP_ULEB (start, end);
+                 READ_ULEB (reg, start, block_end);
+                 SKIP_ULEB (start, block_end);
                  if (frame_need_space (fc, reg) >= 0)
                    fc->col_type[reg] = DW_CFA_undefined;
                  break;
@@ -9028,7 +9034,6 @@ display_debug_frames (struct dwarf_section *section,
 
       while (start < block_end)
        {
-         unsigned char * tmp;
          unsigned op, opa;
          unsigned long ul, roffs;
          /* Note: It is tempting to use an unsigned long for 'reg' but there
@@ -9066,7 +9071,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_offset:
-             READ_ULEB (roffs, start, end);
+             READ_ULEB (roffs, start, block_end);
              if (opa >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9104,7 +9109,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_set_loc:
-             vma = get_encoded_value (&start, fc->fde_encoding, section, block_end);
+             vma = get_encoded_value (&start, fc->fde_encoding, section,
+                                      block_end);
              if (do_debug_frames_interp)
                frame_display_row (fc, &need_col_headers, &max_regs);
              else
@@ -9114,7 +9120,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_advance_loc1:
-             SAFE_BYTE_GET_AND_INC (ofs, start, 1, end);
+             SAFE_BYTE_GET_AND_INC (ofs, start, 1, block_end);
              if (do_debug_frames_interp)
                frame_display_row (fc, &need_col_headers, &max_regs);
              else
@@ -9153,8 +9159,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_offset_extended:
-             READ_ULEB (reg, start, end);
-             READ_ULEB (roffs, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_ULEB (roffs, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9169,8 +9175,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_val_offset:
-             READ_ULEB (reg, start, end);
-             READ_ULEB (roffs, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_ULEB (roffs, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9185,7 +9191,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_restore_extended:
-             READ_ULEB (reg, start, end);
+             READ_ULEB (reg, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9207,7 +9213,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_undefined:
-             READ_ULEB (reg, start, end);
+             READ_ULEB (reg, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9221,7 +9227,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_same_value:
-             READ_ULEB (reg, start, end);
+             READ_ULEB (reg, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9235,8 +9241,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_register:
-             READ_ULEB (reg, start, end);
-             READ_ULEB (roffs, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_ULEB (roffs, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9299,8 +9305,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa:
-             READ_ULEB (fc->cfa_reg, start, end);
-             READ_ULEB (fc->cfa_offset, start, end);
+             READ_ULEB (fc->cfa_reg, start, block_end);
+             READ_ULEB (fc->cfa_offset, start, block_end);
              fc->cfa_exp = 0;
              if (! do_debug_frames_interp)
                printf ("  DW_CFA_def_cfa: %s ofs %d\n",
@@ -9308,7 +9314,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa_register:
-             READ_ULEB (fc->cfa_reg, start, end);
+             READ_ULEB (fc->cfa_reg, start, block_end);
              fc->cfa_exp = 0;
              if (! do_debug_frames_interp)
                printf ("  DW_CFA_def_cfa_register: %s\n",
@@ -9316,7 +9322,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa_offset:
-             READ_ULEB (fc->cfa_offset, start, end);
+             READ_ULEB (fc->cfa_offset, start, block_end);
              if (! do_debug_frames_interp)
                printf ("  DW_CFA_def_cfa_offset: %d\n", (int) fc->cfa_offset);
              break;
@@ -9327,8 +9333,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa_expression:
-             READ_ULEB (ul, start, end);
-             if (start >= block_end || ul > (unsigned long) (block_end - start))
+             READ_ULEB (ul, start, block_end);
+             if (ul > (size_t) (block_end - start))
                {
                  printf (_("  DW_CFA_def_cfa_expression: <corrupt len %lu>\n"), ul);
                  break;
@@ -9345,14 +9351,13 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_expression:
-             READ_ULEB (reg, start, end);
-             READ_ULEB (ul, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_ULEB (ul, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
              /* PR 17512: file: 069-133014-0.006.  */
              /* PR 17512: file: 98c02eb4.  */
-             tmp = start + ul;
-             if (start >= block_end || tmp > block_end || tmp < start)
+             if (ul > (size_t) (block_end - start))
                {
                  printf (_("  DW_CFA_expression: <corrupt len %lu>\n"), ul);
                  break;
@@ -9367,16 +9372,15 @@ display_debug_frames (struct dwarf_section *section,
                }
              if (*reg_prefix == '\0')
                fc->col_type[reg] = DW_CFA_expression;
-             start = tmp;
+             start += ul;
              break;
 
            case DW_CFA_val_expression:
-             READ_ULEB (reg, start, end);
-             READ_ULEB (ul, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_ULEB (ul, start, block_end);
              if (reg >= (unsigned int) fc->ncols)
                reg_prefix = bad_reg;
-             tmp = start + ul;
-             if (start >= block_end || tmp > block_end || tmp < start)
+             if (ul > (size_t) (block_end - start))
                {
                  printf ("  DW_CFA_val_expression: <corrupt len %lu>\n", ul);
                  break;
@@ -9391,12 +9395,12 @@ display_debug_frames (struct dwarf_section *section,
                }
              if (*reg_prefix == '\0')
                fc->col_type[reg] = DW_CFA_val_expression;
-             start = tmp;
+             start += ul;
              break;
 
            case DW_CFA_offset_extended_sf:
-             READ_ULEB (reg, start, end);
-             READ_SLEB (l, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_SLEB (l, start, block_end);
              if (frame_need_space (fc, reg) < 0)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9411,8 +9415,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_val_offset_sf:
-             READ_ULEB (reg, start, end);
-             READ_SLEB (l, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_SLEB (l, start, block_end);
              if (frame_need_space (fc, reg) < 0)
                reg_prefix = bad_reg;
              if (! do_debug_frames_interp || *reg_prefix != '\0')
@@ -9427,8 +9431,8 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa_sf:
-             READ_ULEB (fc->cfa_reg, start, end);
-             READ_ULEB (fc->cfa_offset, start, end);
+             READ_ULEB (fc->cfa_reg, start, block_end);
+             READ_ULEB (fc->cfa_offset, start, block_end);
              fc->cfa_offset = fc->cfa_offset * fc->data_factor;
              fc->cfa_exp = 0;
              if (! do_debug_frames_interp)
@@ -9437,7 +9441,7 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_def_cfa_offset_sf:
-             READ_ULEB (fc->cfa_offset, start, end);
+             READ_ULEB (fc->cfa_offset, start, block_end);
              fc->cfa_offset *= fc->data_factor;
              if (! do_debug_frames_interp)
                printf ("  DW_CFA_def_cfa_offset_sf: %d\n", (int) fc->cfa_offset);
@@ -9462,14 +9466,14 @@ display_debug_frames (struct dwarf_section *section,
              break;
 
            case DW_CFA_GNU_args_size:
-             READ_ULEB (ul, start, end);
+             READ_ULEB (ul, start, block_end);
              if (! do_debug_frames_interp)
                printf ("  DW_CFA_GNU_args_size: %ld\n", ul);
              break;
 
            case DW_CFA_GNU_negative_offset_extended:
-             READ_ULEB (reg, start, end);
-             READ_SLEB (l, start, end);
+             READ_ULEB (reg, start, block_end);
+             READ_SLEB (l, start, block_end);
              l = - l;
              if (frame_need_space (fc, reg) < 0)
                reg_prefix = bad_reg;