diff options
author | Alan Modra <amodra@gmail.com> | 2021-05-15 15:24:03 +0930 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2021-05-15 15:29:36 +0930 |
commit | 5897a389841a67317309213b95bb503d43fbf16c (patch) | |
tree | 7d5f9030f7250ec539bfa16dd6a970d93af98cef /binutils | |
parent | c93c4a85406ebd811ec0b29f1dec882cbba56659 (diff) | |
download | fsf-binutils-gdb-5897a389841a67317309213b95bb503d43fbf16c.zip fsf-binutils-gdb-5897a389841a67317309213b95bb503d43fbf16c.tar.gz fsf-binutils-gdb-5897a389841a67317309213b95bb503d43fbf16c.tar.bz2 |
display_debug_frames
* 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.
Diffstat (limited to 'binutils')
-rw-r--r-- | binutils/ChangeLog | 7 | ||||
-rw-r--r-- | binutils/dwarf.c | 210 |
2 files changed, 114 insertions, 103 deletions
diff --git a/binutils/ChangeLog b/binutils/ChangeLog index f2d363e..29d4406 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,5 +1,12 @@ 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 pointer is not bumped past end. diff --git a/binutils/dwarf.c b/binutils/dwarf.c index 93e6d73..d2af05a 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -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; |