From dbb3fbbb1a3b387a0d33dfd5764cf92bc3da7f41 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Thu, 12 Feb 2015 16:45:11 +0000 Subject: [PATCH] Fix memory access violations triggered by running addr2line on fuzzed binaries. PR binutils/17512 * dwarf.c (read_1_byte, read_1_signed_byte, read_2_bytes) (read_4_bytes, read_8_bytes, read_n_bytes, read_string) (read_indirect_string, read_alt_indirect_string) (read_alt_indirect_ref, read_address, read_abbrevs) (read_attribute_value, read_attribute, decode_line_info) (find_abstract_instance_name, read_rangelist) (scan_unit_for_symbols, parse_comp_unit) (_bfd_dwarf2_find_nearest_line): Harden DWARF reading code. Pass end pointers to reading functions and check for offsets taking pointers out of range. Replace calls to read_*_leb128 with calls to safe_read_leb128. (* elf64-ppc.c (opd_entry_value): Add a check for an overlarge offset. * syms.c (_bfd_stab_section_find_nearest_line): Add checks for computed file_name address being before the start of the string table. --- bfd/ChangeLog | 21 +++ bfd/dwarf2.c | 408 +++++++++++++++++++++++++++++++++--------------- bfd/elf64-ppc.c | 4 + bfd/syms.c | 9 +- 4 files changed, 308 insertions(+), 134 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index d41889404df..1dd74d0d4a7 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,24 @@ +2015-02-12 Nick Clifton + + PR binutils/17512 + * dwarf.c (read_1_byte, read_1_signed_byte, read_2_bytes) + (read_4_bytes, read_8_bytes, read_n_bytes, read_string) + (read_indirect_string, read_alt_indirect_string) + (read_alt_indirect_ref, read_address, read_abbrevs) + (read_attribute_value, read_attribute, decode_line_info) + (find_abstract_instance_name, read_rangelist) + (scan_unit_for_symbols, parse_comp_unit) + (_bfd_dwarf2_find_nearest_line): Harden DWARF reading code. Pass + end pointers to reading functions and check for offsets taking + pointers out of range. Replace calls to read_*_leb128 with calls + to safe_read_leb128. + + (* elf64-ppc.c (opd_entry_value): Add a check for an overlarge + offset. + * syms.c (_bfd_stab_section_find_nearest_line): Add checks for + computed file_name address being before the start of the string + table. + 2015-02-11 H.J. Lu PR ld/17878 diff --git a/bfd/dwarf2.c b/bfd/dwarf2.c index ccc13651c26..d8843121722 100644 --- a/bfd/dwarf2.c +++ b/bfd/dwarf2.c @@ -556,83 +556,124 @@ read_section (bfd * abfd, return TRUE; } -/* VERBATIM - The following function up to the END VERBATIM mark are - copied directly from dwarf2read.c. */ - /* Read dwarf information from a buffer. */ static unsigned int -read_1_byte (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *buf) +read_1_byte (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *buf, bfd_byte *end) { + if (buf + 1 > end) + return 0; return bfd_get_8 (abfd, buf); } static int -read_1_signed_byte (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *buf) +read_1_signed_byte (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *buf, bfd_byte *end) { + if (buf + 1 > end) + return 0; return bfd_get_signed_8 (abfd, buf); } static unsigned int -read_2_bytes (bfd *abfd, bfd_byte *buf) +read_2_bytes (bfd *abfd, bfd_byte *buf, bfd_byte *end) { + if (buf + 2 > end) + return 0; return bfd_get_16 (abfd, buf); } static unsigned int -read_4_bytes (bfd *abfd, bfd_byte *buf) +read_4_bytes (bfd *abfd, bfd_byte *buf, bfd_byte *end) { + if (buf + 4 > end) + return 0; return bfd_get_32 (abfd, buf); } static bfd_uint64_t -read_8_bytes (bfd *abfd, bfd_byte *buf) +read_8_bytes (bfd *abfd, bfd_byte *buf, bfd_byte *end) { + if (buf + 8 > end) + return 0; return bfd_get_64 (abfd, buf); } static bfd_byte * read_n_bytes (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *buf, + bfd_byte *end, unsigned int size ATTRIBUTE_UNUSED) { + if (buf + size > end) + return NULL; return buf; } +/* Scans a NUL terminated string starting at BUF, returning a pointer to it. + Returns the number of characters in the string, *including* the NUL byte, + in BYTES_READ_PTR. This value is set even if the function fails. Bytes + at or beyond BUF_END will not be read. Returns NULL if there was a + problem, or if the string is empty. */ + static char * -read_string (bfd *abfd ATTRIBUTE_UNUSED, - bfd_byte *buf, - unsigned int *bytes_read_ptr) +read_string (bfd * abfd ATTRIBUTE_UNUSED, + bfd_byte * buf, + bfd_byte * buf_end, + unsigned int * bytes_read_ptr) { - /* Return a pointer to the embedded string. */ - char *str = (char *) buf; + bfd_byte *str = buf; + + if (buf >= buf_end) + { + * bytes_read_ptr = 0; + return NULL; + } if (*str == '\0') { - *bytes_read_ptr = 1; + * bytes_read_ptr = 1; return NULL; } - *bytes_read_ptr = strlen (str) + 1; - return str; + while (buf < buf_end) + if (* buf ++ == 0) + { + * bytes_read_ptr = buf - str; + return (char *) str; + } + + * bytes_read_ptr = buf - str; + return NULL; } -/* END VERBATIM */ +/* Reads an offset from BUF and then locates the string at this offset + inside the debug string section. Returns a pointer to the string. + Returns the number of bytes read from BUF, *not* the length of the string, + in BYTES_READ_PTR. This value is set even if the function fails. Bytes + at or beyond BUF_END will not be read from BUF. Returns NULL if there was + a problem, or if the string is empty. Does not check for NUL termination + of the string. */ static char * read_indirect_string (struct comp_unit * unit, bfd_byte * buf, + bfd_byte * buf_end, unsigned int * bytes_read_ptr) { bfd_uint64_t offset; struct dwarf2_debug *stash = unit->stash; char *str; + if (buf + unit->offset_size > buf_end) + { + * bytes_read_ptr = 0; + return NULL; + } + if (unit->offset_size == 4) - offset = read_4_bytes (unit->abfd, buf); + offset = read_4_bytes (unit->abfd, buf, buf_end); else - offset = read_8_bytes (unit->abfd, buf); + offset = read_8_bytes (unit->abfd, buf, buf_end); *bytes_read_ptr = unit->offset_size; @@ -641,6 +682,8 @@ read_indirect_string (struct comp_unit * unit, &stash->dwarf_str_buffer, &stash->dwarf_str_size)) return NULL; + if (offset >= stash->dwarf_str_size) + return NULL; str = (char *) stash->dwarf_str_buffer + offset; if (*str == '\0') return NULL; @@ -654,16 +697,23 @@ read_indirect_string (struct comp_unit * unit, static char * read_alt_indirect_string (struct comp_unit * unit, bfd_byte * buf, + bfd_byte * buf_end, unsigned int * bytes_read_ptr) { bfd_uint64_t offset; struct dwarf2_debug *stash = unit->stash; char *str; + if (buf + unit->offset_size > buf_end) + { + * bytes_read_ptr = 0; + return NULL; + } + if (unit->offset_size == 4) - offset = read_4_bytes (unit->abfd, buf); + offset = read_4_bytes (unit->abfd, buf, buf_end); else - offset = read_8_bytes (unit->abfd, buf); + offset = read_8_bytes (unit->abfd, buf, buf_end); *bytes_read_ptr = unit->offset_size; @@ -696,6 +746,8 @@ read_alt_indirect_string (struct comp_unit * unit, &stash->alt_dwarf_str_size)) return NULL; + if (offset >= stash->alt_dwarf_str_size) + return NULL; str = (char *) stash->alt_dwarf_str_buffer + offset; if (*str == '\0') return NULL; @@ -742,17 +794,22 @@ read_alt_indirect_ref (struct comp_unit * unit, &stash->alt_dwarf_info_size)) return NULL; + if (offset >= stash->alt_dwarf_info_size) + return NULL; return stash->alt_dwarf_info_buffer + offset; } static bfd_uint64_t -read_address (struct comp_unit *unit, bfd_byte *buf) +read_address (struct comp_unit *unit, bfd_byte *buf, bfd_byte * buf_end) { int signed_vma = 0; if (bfd_get_flavour (unit->abfd) == bfd_target_elf_flavour) signed_vma = get_elf_backend_data (unit->abfd)->sign_extend_vma; + if (buf + unit->addr_size > buf_end) + return 0; + if (signed_vma) { switch (unit->addr_size) @@ -815,6 +872,7 @@ read_abbrevs (bfd *abfd, bfd_uint64_t offset, struct dwarf2_debug *stash) { struct abbrev_info **abbrevs; bfd_byte *abbrev_ptr; + bfd_byte *abbrev_end; struct abbrev_info *cur_abbrev; unsigned int abbrev_number, bytes_read, abbrev_name; unsigned int abbrev_form, hash_number; @@ -825,13 +883,17 @@ read_abbrevs (bfd *abfd, bfd_uint64_t offset, struct dwarf2_debug *stash) &stash->dwarf_abbrev_buffer, &stash->dwarf_abbrev_size)) return NULL; + if (offset >= stash->dwarf_abbrev_size) + return NULL; + amt = sizeof (struct abbrev_info*) * ABBREV_HASH_SIZE; abbrevs = (struct abbrev_info **) bfd_zalloc (abfd, amt); if (abbrevs == NULL) return NULL; abbrev_ptr = stash->dwarf_abbrev_buffer + offset; - abbrev_number = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_end = stash->dwarf_abbrev_buffer + stash->dwarf_abbrev_size; + abbrev_number = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; /* Loop until we reach an abbrev number of 0. */ @@ -845,15 +907,15 @@ read_abbrevs (bfd *abfd, bfd_uint64_t offset, struct dwarf2_debug *stash) /* Read in abbrev header. */ cur_abbrev->number = abbrev_number; cur_abbrev->tag = (enum dwarf_tag) - read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; - cur_abbrev->has_children = read_1_byte (abfd, abbrev_ptr); + cur_abbrev->has_children = read_1_byte (abfd, abbrev_ptr, abbrev_end); abbrev_ptr += 1; /* Now read in declarations. */ - abbrev_name = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_name = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; - abbrev_form = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_form = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; while (abbrev_name) @@ -888,9 +950,9 @@ read_abbrevs (bfd *abfd, bfd_uint64_t offset, struct dwarf2_debug *stash) = (enum dwarf_attribute) abbrev_name; cur_abbrev->attrs[cur_abbrev->num_attrs++].form = (enum dwarf_form) abbrev_form; - abbrev_name = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_name = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; - abbrev_form = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_form = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; } @@ -908,9 +970,9 @@ read_abbrevs (bfd *abfd, bfd_uint64_t offset, struct dwarf2_debug *stash) if ((unsigned int) (abbrev_ptr - stash->dwarf_abbrev_buffer) >= stash->dwarf_abbrev_size) break; - abbrev_number = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); + abbrev_number = safe_read_leb128 (abfd, abbrev_ptr, &bytes_read, FALSE, abbrev_end); abbrev_ptr += bytes_read; - if (lookup_abbrev (abbrev_number,abbrevs) != NULL) + if (lookup_abbrev (abbrev_number, abbrevs) != NULL) break; } @@ -925,19 +987,29 @@ is_str_attr (enum dwarf_form form) return form == DW_FORM_string || form == DW_FORM_strp || form == DW_FORM_GNU_strp_alt; } -/* Read an attribute value described by an attribute form. */ +/* Read and fill in the value of attribute ATTR as described by FORM. + Read data starting from INFO_PTR, but never at or beyond INFO_PTR_END. + Returns an updated INFO_PTR taking into account the amount of data read. */ static bfd_byte * -read_attribute_value (struct attribute *attr, - unsigned form, - struct comp_unit *unit, - bfd_byte *info_ptr) +read_attribute_value (struct attribute * attr, + unsigned form, + struct comp_unit * unit, + bfd_byte * info_ptr, + bfd_byte * info_ptr_end) { bfd *abfd = unit->abfd; unsigned int bytes_read; struct dwarf_block *blk; bfd_size_type amt; + if (info_ptr >= info_ptr_end) + { + (*_bfd_error_handler) (_("Dwarf Error: Info pointer extends beyond end of attributes")); + bfd_set_error (bfd_error_bad_value); + return info_ptr; + } + attr->form = (enum dwarf_form) form; switch (form) @@ -948,23 +1020,23 @@ read_attribute_value (struct attribute *attr, if (unit->version == 3 || unit->version == 4) { if (unit->offset_size == 4) - attr->u.val = read_4_bytes (unit->abfd, info_ptr); + attr->u.val = read_4_bytes (unit->abfd, info_ptr, info_ptr_end); else - attr->u.val = read_8_bytes (unit->abfd, info_ptr); + attr->u.val = read_8_bytes (unit->abfd, info_ptr, info_ptr_end); info_ptr += unit->offset_size; break; } /* FALLTHROUGH */ case DW_FORM_addr: - attr->u.val = read_address (unit, info_ptr); + attr->u.val = read_address (unit, info_ptr, info_ptr_end); info_ptr += unit->addr_size; break; case DW_FORM_GNU_ref_alt: case DW_FORM_sec_offset: if (unit->offset_size == 4) - attr->u.val = read_4_bytes (unit->abfd, info_ptr); + attr->u.val = read_4_bytes (unit->abfd, info_ptr, info_ptr_end); else - attr->u.val = read_8_bytes (unit->abfd, info_ptr); + attr->u.val = read_8_bytes (unit->abfd, info_ptr, info_ptr_end); info_ptr += unit->offset_size; break; case DW_FORM_block2: @@ -972,9 +1044,9 @@ read_attribute_value (struct attribute *attr, blk = (struct dwarf_block *) bfd_alloc (abfd, amt); if (blk == NULL) return NULL; - blk->size = read_2_bytes (abfd, info_ptr); + blk->size = read_2_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 2; - blk->data = read_n_bytes (abfd, info_ptr, blk->size); + blk->data = read_n_bytes (abfd, info_ptr, info_ptr_end, blk->size); info_ptr += blk->size; attr->u.blk = blk; break; @@ -983,34 +1055,34 @@ read_attribute_value (struct attribute *attr, blk = (struct dwarf_block *) bfd_alloc (abfd, amt); if (blk == NULL) return NULL; - blk->size = read_4_bytes (abfd, info_ptr); + blk->size = read_4_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 4; - blk->data = read_n_bytes (abfd, info_ptr, blk->size); + blk->data = read_n_bytes (abfd, info_ptr, info_ptr_end, blk->size); info_ptr += blk->size; attr->u.blk = blk; break; case DW_FORM_data2: - attr->u.val = read_2_bytes (abfd, info_ptr); + attr->u.val = read_2_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 2; break; case DW_FORM_data4: - attr->u.val = read_4_bytes (abfd, info_ptr); + attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 4; break; case DW_FORM_data8: - attr->u.val = read_8_bytes (abfd, info_ptr); + attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 8; break; case DW_FORM_string: - attr->u.str = read_string (abfd, info_ptr, &bytes_read); + attr->u.str = read_string (abfd, info_ptr, info_ptr_end, &bytes_read); info_ptr += bytes_read; break; case DW_FORM_strp: - attr->u.str = read_indirect_string (unit, info_ptr, &bytes_read); + attr->u.str = read_indirect_string (unit, info_ptr, info_ptr_end, &bytes_read); info_ptr += bytes_read; break; case DW_FORM_GNU_strp_alt: - attr->u.str = read_alt_indirect_string (unit, info_ptr, &bytes_read); + attr->u.str = read_alt_indirect_string (unit, info_ptr, info_ptr_end, &bytes_read); info_ptr += bytes_read; break; case DW_FORM_exprloc: @@ -1019,9 +1091,9 @@ read_attribute_value (struct attribute *attr, blk = (struct dwarf_block *) bfd_alloc (abfd, amt); if (blk == NULL) return NULL; - blk->size = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + blk->size = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; - blk->data = read_n_bytes (abfd, info_ptr, blk->size); + blk->data = read_n_bytes (abfd, info_ptr, info_ptr_end, blk->size); info_ptr += blk->size; attr->u.blk = blk; break; @@ -1030,59 +1102,59 @@ read_attribute_value (struct attribute *attr, blk = (struct dwarf_block *) bfd_alloc (abfd, amt); if (blk == NULL) return NULL; - blk->size = read_1_byte (abfd, info_ptr); + blk->size = read_1_byte (abfd, info_ptr, info_ptr_end); info_ptr += 1; - blk->data = read_n_bytes (abfd, info_ptr, blk->size); + blk->data = read_n_bytes (abfd, info_ptr, info_ptr_end, blk->size); info_ptr += blk->size; attr->u.blk = blk; break; case DW_FORM_data1: - attr->u.val = read_1_byte (abfd, info_ptr); + attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end); info_ptr += 1; break; case DW_FORM_flag: - attr->u.val = read_1_byte (abfd, info_ptr); + attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end); info_ptr += 1; break; case DW_FORM_flag_present: attr->u.val = 1; break; case DW_FORM_sdata: - attr->u.sval = read_signed_leb128 (abfd, info_ptr, &bytes_read); + attr->u.sval = safe_read_leb128 (abfd, info_ptr, &bytes_read, TRUE, info_ptr_end); info_ptr += bytes_read; break; case DW_FORM_udata: - attr->u.val = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + attr->u.val = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; break; case DW_FORM_ref1: - attr->u.val = read_1_byte (abfd, info_ptr); + attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end); info_ptr += 1; break; case DW_FORM_ref2: - attr->u.val = read_2_bytes (abfd, info_ptr); + attr->u.val = read_2_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 2; break; case DW_FORM_ref4: - attr->u.val = read_4_bytes (abfd, info_ptr); + attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 4; break; case DW_FORM_ref8: - attr->u.val = read_8_bytes (abfd, info_ptr); + attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 8; break; case DW_FORM_ref_sig8: - attr->u.val = read_8_bytes (abfd, info_ptr); + attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end); info_ptr += 8; break; case DW_FORM_ref_udata: - attr->u.val = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + attr->u.val = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; break; case DW_FORM_indirect: - form = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + form = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; - info_ptr = read_attribute_value (attr, form, unit, info_ptr); + info_ptr = read_attribute_value (attr, form, unit, info_ptr, info_ptr_end); break; default: (*_bfd_error_handler) (_("Dwarf Error: Invalid or unhandled FORM value: %#x."), @@ -1096,13 +1168,14 @@ read_attribute_value (struct attribute *attr, /* Read an attribute described by an abbreviated attribute. */ static bfd_byte * -read_attribute (struct attribute *attr, - struct attr_abbrev *abbrev, - struct comp_unit *unit, - bfd_byte *info_ptr) +read_attribute (struct attribute * attr, + struct attr_abbrev * abbrev, + struct comp_unit * unit, + bfd_byte * info_ptr, + bfd_byte * info_ptr_end) { attr->name = abbrev->name; - info_ptr = read_attribute_value (attr, abbrev->form, unit, info_ptr); + info_ptr = read_attribute_value (attr, abbrev->form, unit, info_ptr, info_ptr_end); return info_ptr; } @@ -1388,6 +1461,8 @@ concat_filename (struct line_info_table *table, unsigned int file) size_t len; if (table->files[file - 1].dir + /* PR 17512: file: 0317e960. */ + && table->files[file - 1].dir <= table->num_dirs /* PR 17512: file: 7f3d2e4b. */ && table->dirs != NULL) subdir_name = table->dirs[table->files[file - 1].dir - 1]; @@ -1610,27 +1685,47 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) table->lcl_head = NULL; + if (stash->dwarf_line_size < 16) + { + (*_bfd_error_handler) + (_("Dwarf Error: Line info section is too small (%ld)"), + (long) stash->dwarf_line_size); + bfd_set_error (bfd_error_bad_value); + return NULL; + } line_ptr = stash->dwarf_line_buffer + unit->line_offset; + line_end = stash->dwarf_line_buffer + stash->dwarf_line_size; /* Read in the prologue. */ - lh.total_length = read_4_bytes (abfd, line_ptr); + lh.total_length = read_4_bytes (abfd, line_ptr, line_end); line_ptr += 4; offset_size = 4; if (lh.total_length == 0xffffffff) { - lh.total_length = read_8_bytes (abfd, line_ptr); + lh.total_length = read_8_bytes (abfd, line_ptr, line_end); line_ptr += 8; offset_size = 8; } else if (lh.total_length == 0 && unit->addr_size == 8) { /* Handle (non-standard) 64-bit DWARF2 formats. */ - lh.total_length = read_4_bytes (abfd, line_ptr); + lh.total_length = read_4_bytes (abfd, line_ptr, line_end); line_ptr += 4; offset_size = 8; } + + if (lh.total_length > stash->dwarf_line_size) + { + (*_bfd_error_handler) + (_("Dwarf Error: Line info data is bigger (0x%lx) than the section (0x%lx)"), + (long) lh.total_length, (long) stash->dwarf_line_size); + bfd_set_error (bfd_error_bad_value); + return NULL; + } + line_end = line_ptr + lh.total_length; - lh.version = read_2_bytes (abfd, line_ptr); + + lh.version = read_2_bytes (abfd, line_ptr, line_end); if (lh.version < 2 || lh.version > 4) { (*_bfd_error_handler) @@ -1639,20 +1734,32 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) return NULL; } line_ptr += 2; + + if (line_ptr + offset_size + (lh.version >=4 ? 6 : 5) >= line_end) + { + (*_bfd_error_handler) + (_("Dwarf Error: Ran out of room reading prologue")); + bfd_set_error (bfd_error_bad_value); + return NULL; + } + if (offset_size == 4) - lh.prologue_length = read_4_bytes (abfd, line_ptr); + lh.prologue_length = read_4_bytes (abfd, line_ptr, line_end); else - lh.prologue_length = read_8_bytes (abfd, line_ptr); + lh.prologue_length = read_8_bytes (abfd, line_ptr, line_end); line_ptr += offset_size; - lh.minimum_instruction_length = read_1_byte (abfd, line_ptr); + + lh.minimum_instruction_length = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; + if (lh.version >= 4) { - lh.maximum_ops_per_insn = read_1_byte (abfd, line_ptr); + lh.maximum_ops_per_insn = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; } else lh.maximum_ops_per_insn = 1; + if (lh.maximum_ops_per_insn == 0) { (*_bfd_error_handler) @@ -1660,14 +1767,26 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) bfd_set_error (bfd_error_bad_value); return NULL; } - lh.default_is_stmt = read_1_byte (abfd, line_ptr); + + lh.default_is_stmt = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; - lh.line_base = read_1_signed_byte (abfd, line_ptr); + + lh.line_base = read_1_signed_byte (abfd, line_ptr, line_end); line_ptr += 1; - lh.line_range = read_1_byte (abfd, line_ptr); + + lh.line_range = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; - lh.opcode_base = read_1_byte (abfd, line_ptr); + + lh.opcode_base = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; + + if (line_ptr + (lh.opcode_base - 1) >= line_end) + { + (*_bfd_error_handler) (_("Dwarf Error: Ran out of room reading opcodes")); + bfd_set_error (bfd_error_bad_value); + return NULL; + } + amt = lh.opcode_base * sizeof (unsigned char); lh.standard_opcode_lengths = (unsigned char *) bfd_alloc (abfd, amt); @@ -1675,12 +1794,12 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) for (i = 1; i < lh.opcode_base; ++i) { - lh.standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr); + lh.standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; } /* Read directory table. */ - while ((cur_dir = read_string (abfd, line_ptr, &bytes_read)) != NULL) + while ((cur_dir = read_string (abfd, line_ptr, line_end, &bytes_read)) != NULL) { line_ptr += bytes_read; @@ -1703,7 +1822,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) line_ptr += bytes_read; /* Read file name table. */ - while ((cur_file = read_string (abfd, line_ptr, &bytes_read)) != NULL) + while ((cur_file = read_string (abfd, line_ptr, line_end, &bytes_read)) != NULL) { line_ptr += bytes_read; @@ -1722,13 +1841,11 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) table->files[table->num_files].name = cur_file; table->files[table->num_files].dir = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; - table->files[table->num_files].time = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + table->files[table->num_files].time = safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; - table->files[table->num_files].size = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + table->files[table->num_files].size = safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; table->num_files++; } @@ -1758,13 +1875,15 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) /* Decode the table. */ while (! end_sequence) { - op_code = read_1_byte (abfd, line_ptr); + op_code = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; if (op_code >= lh.opcode_base) { /* Special operand. */ adj_opcode = op_code - lh.opcode_base; + if (lh.line_range == 0) + goto line_fail; if (lh.maximum_ops_per_insn == 1) address += (adj_opcode / lh.line_range * lh.minimum_instruction_length); @@ -1790,9 +1909,9 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) else switch (op_code) { case DW_LNS_extended_op: - exop_len = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + exop_len = safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; - extended_op = read_1_byte (abfd, line_ptr); + extended_op = read_1_byte (abfd, line_ptr, line_end); line_ptr += 1; switch (extended_op) @@ -1811,12 +1930,12 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) goto line_fail; break; case DW_LNE_set_address: - address = read_address (unit, line_ptr); + address = read_address (unit, line_ptr, line_end); op_index = 0; line_ptr += unit->addr_size; break; case DW_LNE_define_file: - cur_file = read_string (abfd, line_ptr, &bytes_read); + cur_file = read_string (abfd, line_ptr, line_end, &bytes_read); line_ptr += bytes_read; if ((table->num_files % FILE_ALLOC_CHUNK) == 0) { @@ -1831,19 +1950,19 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) } table->files[table->num_files].name = cur_file; table->files[table->num_files].dir = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; table->files[table->num_files].time = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; table->files[table->num_files].size = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; table->num_files++; break; case DW_LNE_set_discriminator: discriminator = - read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; break; case DW_LNE_HP_source_file_correlation: @@ -1872,12 +1991,12 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) case DW_LNS_advance_pc: if (lh.maximum_ops_per_insn == 1) address += (lh.minimum_instruction_length - * read_unsigned_leb128 (abfd, line_ptr, - &bytes_read)); + * safe_read_leb128 (abfd, line_ptr, &bytes_read, + FALSE, line_end)); else { - bfd_vma adjust = read_unsigned_leb128 (abfd, line_ptr, - &bytes_read); + bfd_vma adjust = safe_read_leb128 (abfd, line_ptr, &bytes_read, + FALSE, line_end); address = ((op_index + adjust) / lh.maximum_ops_per_insn * lh.minimum_instruction_length); op_index = (op_index + adjust) % lh.maximum_ops_per_insn; @@ -1885,7 +2004,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) line_ptr += bytes_read; break; case DW_LNS_advance_line: - line += read_signed_leb128 (abfd, line_ptr, &bytes_read); + line += safe_read_leb128 (abfd, line_ptr, &bytes_read, TRUE, line_end); line_ptr += bytes_read; break; case DW_LNS_set_file: @@ -1894,7 +2013,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) /* The file and directory tables are 0 based, the references are 1 based. */ - file = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + file = safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; if (filename) free (filename); @@ -1902,7 +2021,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) break; } case DW_LNS_set_column: - column = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + column = safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; break; case DW_LNS_negate_stmt: @@ -1924,7 +2043,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) } break; case DW_LNS_fixed_advance_pc: - address += read_2_bytes (abfd, line_ptr); + address += read_2_bytes (abfd, line_ptr, line_end); op_index = 0; line_ptr += 2; break; @@ -1932,7 +2051,7 @@ decode_line_info (struct comp_unit *unit, struct dwarf2_debug *stash) /* Unknown standard opcode, ignore it. */ for (i = 0; i < lh.standard_opcode_lengths[op_code]; i++) { - (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + (void) safe_read_leb128 (abfd, line_ptr, &bytes_read, FALSE, line_end); line_ptr += bytes_read; } break; @@ -2161,6 +2280,7 @@ find_abstract_instance_name (struct comp_unit *unit, { bfd *abfd = unit->abfd; bfd_byte *info_ptr; + bfd_byte *info_ptr_end; unsigned int abbrev_number, bytes_read, i; struct abbrev_info *abbrev; bfd_uint64_t die_ref = attr_ptr->u.val; @@ -2177,6 +2297,7 @@ find_abstract_instance_name (struct comp_unit *unit, abort (); info_ptr = unit->sec_info_ptr + die_ref; + info_ptr_end = unit->end_ptr; /* Now find the CU containing this pointer. */ if (info_ptr >= unit->info_ptr_unit && info_ptr < unit->end_ptr) @@ -2210,13 +2331,18 @@ find_abstract_instance_name (struct comp_unit *unit, bfd_set_error (bfd_error_bad_value); return NULL; } + info_ptr_end = unit->stash->alt_dwarf_info_buffer + unit->stash->alt_dwarf_info_size; + /* FIXME: Do we need to locate the correct CU, in a similar fashion to the code in the DW_FORM_ref_addr case above ? */ } else - info_ptr = unit->info_ptr_unit + die_ref; + { + info_ptr = unit->info_ptr_unit + die_ref; + info_ptr_end = unit->end_ptr; + } - abbrev_number = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + abbrev_number = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; if (abbrev_number) @@ -2233,7 +2359,7 @@ find_abstract_instance_name (struct comp_unit *unit, for (i = 0; i < abbrev->num_attrs; ++i) { info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, - info_ptr); + info_ptr, info_ptr_end); if (info_ptr == NULL) break; switch (attr.name) @@ -2275,6 +2401,7 @@ read_rangelist (struct comp_unit *unit, struct arange *arange, bfd_uint64_t offset) { bfd_byte *ranges_ptr; + bfd_byte *ranges_end; bfd_vma base_address = unit->base_address; if (! unit->stash->dwarf_ranges_buffer) @@ -2282,16 +2409,24 @@ read_rangelist (struct comp_unit *unit, struct arange *arange, if (! read_debug_ranges (unit)) return FALSE; } - ranges_ptr = unit->stash->dwarf_ranges_buffer + offset; + ranges_ptr = unit->stash->dwarf_ranges_buffer + offset; + if (ranges_ptr < unit->stash->dwarf_ranges_buffer) + return FALSE; + ranges_end = unit->stash->dwarf_ranges_buffer + unit->stash->dwarf_ranges_size; + for (;;) { bfd_vma low_pc; bfd_vma high_pc; - low_pc = read_address (unit, ranges_ptr); + /* PR 17512: file: 62cada7d. */ + if (ranges_ptr + 2 * unit->addr_size >= ranges_end) + return FALSE; + + low_pc = read_address (unit, ranges_ptr, ranges_end); ranges_ptr += unit->addr_size; - high_pc = read_address (unit, ranges_ptr); + high_pc = read_address (unit, ranges_ptr, ranges_end); ranges_ptr += unit->addr_size; if (low_pc == 0 && high_pc == 0) @@ -2318,6 +2453,7 @@ scan_unit_for_symbols (struct comp_unit *unit) { bfd *abfd = unit->abfd; bfd_byte *info_ptr = unit->first_child_die_ptr; + bfd_byte *info_ptr_end = unit->stash->info_ptr_end; int nesting_level = 1; struct funcinfo **nested_funcs; int nested_funcs_size; @@ -2343,10 +2479,10 @@ scan_unit_for_symbols (struct comp_unit *unit) bfd_boolean high_pc_relative = FALSE; /* PR 17512: file: 9f405d9d. */ - if (info_ptr >= unit->stash->info_ptr_end) + if (info_ptr >= info_ptr_end) goto fail; - abbrev_number = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + abbrev_number = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, info_ptr_end); info_ptr += bytes_read; if (! abbrev_number) @@ -2410,7 +2546,7 @@ scan_unit_for_symbols (struct comp_unit *unit) for (i = 0; i < abbrev->num_attrs; ++i) { - info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr); + info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr, info_ptr_end); if (info_ptr == NULL) goto fail; @@ -2608,15 +2744,15 @@ parse_comp_unit (struct dwarf2_debug *stash, bfd *abfd = stash->bfd_ptr; bfd_boolean high_pc_relative = FALSE; - version = read_2_bytes (abfd, info_ptr); + version = read_2_bytes (abfd, info_ptr, end_ptr); info_ptr += 2; BFD_ASSERT (offset_size == 4 || offset_size == 8); if (offset_size == 4) - abbrev_offset = read_4_bytes (abfd, info_ptr); + abbrev_offset = read_4_bytes (abfd, info_ptr, end_ptr); else - abbrev_offset = read_8_bytes (abfd, info_ptr); + abbrev_offset = read_8_bytes (abfd, info_ptr, end_ptr); info_ptr += offset_size; - addr_size = read_1_byte (abfd, info_ptr); + addr_size = read_1_byte (abfd, info_ptr, end_ptr); info_ptr += 1; if (version != 2 && version != 3 && version != 4) @@ -2653,7 +2789,7 @@ parse_comp_unit (struct dwarf2_debug *stash, if (! abbrevs) return 0; - abbrev_number = read_unsigned_leb128 (abfd, info_ptr, &bytes_read); + abbrev_number = safe_read_leb128 (abfd, info_ptr, &bytes_read, FALSE, end_ptr); info_ptr += bytes_read; if (! abbrev_number) { @@ -2688,7 +2824,7 @@ parse_comp_unit (struct dwarf2_debug *stash, for (i = 0; i < abbrev->num_attrs; ++i) { - info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr); + info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr, end_ptr); if (info_ptr == NULL) return NULL; @@ -3838,13 +3974,13 @@ _bfd_dwarf2_find_nearest_line (bfd *abfd, unsigned int offset_size = addr_size; bfd_byte *info_ptr_unit = stash->info_ptr; - length = read_4_bytes (stash->bfd_ptr, stash->info_ptr); + length = read_4_bytes (stash->bfd_ptr, stash->info_ptr, stash->info_ptr_end); /* A 0xffffff length is the DWARF3 way of indicating we use 64-bit offsets, instead of 32-bit offsets. */ if (length == 0xffffffff) { offset_size = 8; - length = read_8_bytes (stash->bfd_ptr, stash->info_ptr + 4); + length = read_8_bytes (stash->bfd_ptr, stash->info_ptr + 4, stash->info_ptr_end); stash->info_ptr += 12; } /* A zero length is the IRIX way of indicating 64-bit offsets, @@ -3853,7 +3989,7 @@ _bfd_dwarf2_find_nearest_line (bfd *abfd, else if (length == 0) { offset_size = 8; - length = read_4_bytes (stash->bfd_ptr, stash->info_ptr + 4); + length = read_4_bytes (stash->bfd_ptr, stash->info_ptr + 4, stash->info_ptr_end); stash->info_ptr += 8; } /* In the absence of the hints above, we assume 32-bit DWARF2 @@ -3875,14 +4011,26 @@ _bfd_dwarf2_find_nearest_line (bfd *abfd, if (length > 0) { + bfd_byte * new_ptr; + each = parse_comp_unit (stash, length, info_ptr_unit, offset_size); if (!each) /* The dwarf information is damaged, don't trust it any more. */ break; - stash->info_ptr += length; + new_ptr = stash->info_ptr + length; + /* PR 17512: file: 1500698c. */ + if (new_ptr < stash->info_ptr) + { + /* A corrupt length value - do not trust the info any more. */ + found = FALSE; + break; + } + else + stash->info_ptr = new_ptr; + if (stash->all_comp_units) stash->all_comp_units->prev_unit = each; else diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index f821231d64a..542ed1ca31f 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -5951,6 +5951,10 @@ opd_entry_value (asection *opd_sec, ppc64_elf_tdata (opd_bfd)->opd.contents = contents; } + /* PR 17512: file: 64b9dfbb. */ + if (offset > bfd_section_size (opd_bfd, opd_sec)) + return (bfd_vma) -1; + val = bfd_get_64 (opd_bfd, contents + offset); if (code_sec != NULL) { diff --git a/bfd/syms.c b/bfd/syms.c index 0b0d26ded60..9d7a1f43c13 100644 --- a/bfd/syms.c +++ b/bfd/syms.c @@ -1192,7 +1192,7 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, { nul_fun = stab; nul_str = str; - if (file_name >= (char *) info->strs + strsize) + if (file_name >= (char *) info->strs + strsize || file_name < (char *) str) file_name = NULL; if (stab + STABSIZE + TYPEOFF < info->stabs + stabsize && *(stab + STABSIZE + TYPEOFF) == (bfd_byte) N_SO) @@ -1203,7 +1203,7 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, directory_name = file_name; file_name = ((char *) str + bfd_get_32 (abfd, stab + STRDXOFF)); - if (file_name >= (char *) info->strs + strsize) + if (file_name >= (char *) info->strs + strsize || file_name < (char *) str) file_name = NULL; } } @@ -1213,7 +1213,8 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, /* The name of an include file. */ file_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF); /* PR 17512: file: 0c680a1f. */ - if (file_name >= (char *) info->strs + strsize) + /* PR 17512: file: 5da8aec4. */ + if (file_name >= (char *) info->strs + strsize || file_name < (char *) str) file_name = NULL; break; @@ -1331,7 +1332,7 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, if (val <= offset) { file_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF); - if (file_name >= (char *) info->strs + strsize) + if (file_name >= (char *) info->strs + strsize || file_name < (char *) str) file_name = NULL; *pline = 0; } -- 2.30.2