diff options
author | Nick Clifton <nickc@redhat.com> | 2014-11-07 13:39:45 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2014-11-07 13:39:45 +0000 |
commit | 071436c6e94be13904438b6eb70ee79c73354a61 (patch) | |
tree | 1bd6b97efe937fa1b33f582d82b2a5ee0d549a5f /binutils | |
parent | 56aedec7ab6a1da818ed900827e3a2eb1f5cc5d2 (diff) | |
download | gdb-071436c6e94be13904438b6eb70ee79c73354a61.zip gdb-071436c6e94be13904438b6eb70ee79c73354a61.tar.gz gdb-071436c6e94be13904438b6eb70ee79c73354a61.tar.bz2 |
Add more fixes for inavlid memory accesses triggered by corrupt files.
PR binutils/17531
* readelf.c (get_data): Avoid allocating memory when we know that
the read will fail.
(find_section_by_type): New function.
(get_unwind_section_word): Check for invalid symbol indicies.
Check for invalid reloc types.
(get_32bit_dynamic_section): Add range checks.
(get_64bit_dynamic_section): Add range checks.
(process_dynamic_section): Check for a corrupt time value.
(process_symbol_table): Add range checks.
(dump_section_as_strings): Add string length range checks.
(display_tag_value): Likewise.
(display_arm_attribute): Likewise.
(display_gnu_attribute): Likewise.
(display_tic6x_attribute): Likewise.
(display_msp430x_attribute): Likewise.
(process_mips_specific): Add range check.
Diffstat (limited to 'binutils')
-rw-r--r-- | binutils/ChangeLog | 20 | ||||
-rw-r--r-- | binutils/readelf.c | 297 |
2 files changed, 226 insertions, 91 deletions
diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 6a04543..b0c6bb2 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,23 @@ +2014-11-07 Nick Clifton <nickc@redhat.com> + + PR binutils/17531 + * readelf.c (get_data): Avoid allocating memory when we know that + the read will fail. + (find_section_by_type): New function. + (get_unwind_section_word): Check for invalid symbol indicies. + Check for invalid reloc types. + (get_32bit_dynamic_section): Add range checks. + (get_64bit_dynamic_section): Add range checks. + (process_dynamic_section): Check for a corrupt time value. + (process_symbol_table): Add range checks. + (dump_section_as_strings): Add string length range checks. + (display_tag_value): Likewise. + (display_arm_attribute): Likewise. + (display_gnu_attribute): Likewise. + (display_tic6x_attribute): Likewise. + (display_msp430x_attribute): Likewise. + (process_mips_specific): Add range check. + 2014-11-06 Nick Clifton <nickc@redhat.com> PR binutils/17552, binutils/17533 diff --git a/binutils/readelf.c b/binutils/readelf.c index 089ea13..5bd3a4b 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -322,6 +322,16 @@ get_data (void * var, FILE * file, long offset, size_t size, size_t nmemb, return NULL; } + /* Be kind to memory chekers (eg valgrind, address sanitizer) by not + attempting to allocate memory when the read is bound to fail. */ + if (offset + archive_file_offset + size * nmemb > current_file_size) + { + if (reason) + error (_("Reading 0x%lx bytes extends past end of file for %s\n"), + (unsigned long) (size * nmemb), reason); + return NULL; + } + mvar = var; if (mvar == NULL) { @@ -587,6 +597,21 @@ find_section_by_address (bfd_vma addr) return NULL; } +static Elf_Internal_Shdr * +find_section_by_type (unsigned int type) +{ + unsigned int i; + + for (i = 0; i < elf_header.e_shnum; i++) + { + Elf_Internal_Shdr *sec = section_headers + i; + if (sec->sh_type == type) + return sec; + } + + return NULL; +} + /* Return a pointer to section NAME, or NULL if no such section exists, restricted to the list of sections given in SET. */ @@ -5722,9 +5747,10 @@ process_section_groups (FILE * file) strtab_sec = sec; if (strtab) free (strtab); + strtab = (char *) get_data (NULL, file, strtab_sec->sh_offset, - 1, strtab_sec->sh_size, - _("string table")); + 1, strtab_sec->sh_size, + _("string table")); strtab_size = strtab != NULL ? strtab_sec->sh_size : 0; } group_name = sym->st_name < strtab_size @@ -6135,8 +6161,8 @@ process_relocs (FILE * file) strsec = section_headers + symsec->sh_link; strtab = (char *) get_data (NULL, file, strsec->sh_offset, - 1, strsec->sh_size, - _("string table")); + 1, strsec->sh_size, + _("string table")); strtablen = strtab == NULL ? 0 : strsec->sh_size; } @@ -7067,8 +7093,6 @@ get_unwind_section_word (struct arm_unw_aux_info * aux, if (aux->symtab == NULL) continue; - sym = aux->symtab + ELF32_R_SYM (rp->r_info); - if (arm_sec->rel_type == SHT_REL) { offset = word & 0x7fffffff; @@ -7084,6 +7108,15 @@ get_unwind_section_word (struct arm_unw_aux_info * aux, break; } + /* PR 17531 file: 027-1241568-0.004. */ + if (ELF32_R_SYM (rp->r_info) >= aux->nsyms) + { + error (_("Bad symbol index in unwind relocation (%lu > %lu)\n"), + (unsigned long) ELF32_R_SYM (rp->r_info), aux->nsyms); + break; + } + + sym = aux->symtab + ELF32_R_SYM (rp->r_info); offset += sym->st_value; prelval = offset - (arm_sec->sec->sh_addr + rp->r_offset); @@ -7091,26 +7124,38 @@ get_unwind_section_word (struct arm_unw_aux_info * aux, if (elf_header.e_machine == EM_ARM) { relname = elf_arm_reloc_type (ELF32_R_TYPE (rp->r_info)); + if (relname == NULL) + { + warn (_("Skipping unknown ARM relocation type: %d\n"), + (int) ELF32_R_TYPE (rp->r_info)); + continue; + } if (streq (relname, "R_ARM_NONE")) continue; if (! streq (relname, "R_ARM_PREL31")) { - warn (_("Skipping unexpected relocation type %s\n"), relname); + warn (_("Skipping unexpected ARM relocation type %s\n"), relname); continue; } } else if (elf_header.e_machine == EM_TI_C6000) { relname = elf_tic6x_reloc_type (ELF32_R_TYPE (rp->r_info)); + if (relname == NULL) + { + warn (_("Skipping unknown C6000 relocation type: %d\n"), + (int) ELF32_R_TYPE (rp->r_info)); + continue; + } if (streq (relname, "R_C6000_NONE")) continue; if (! streq (relname, "R_C6000_PREL31")) { - warn (_("Skipping unexpected relocation type %s\n"), relname); + warn (_("Skipping unexpected C6000 relocation type %s\n"), relname); continue; } @@ -8133,11 +8178,11 @@ get_32bit_dynamic_section (FILE * file) if (!edyn) return 0; -/* SGI's ELF has more than one section in the DYNAMIC segment, and we - might not have the luxury of section headers. Look for the DT_NULL - terminator to determine the number of entries. */ + /* SGI's ELF has more than one section in the DYNAMIC segment, and we + might not have the luxury of section headers. Look for the DT_NULL + terminator to determine the number of entries. */ for (ext = edyn, dynamic_nent = 0; - (char *) ext < (char *) edyn + dynamic_size; + (char *) ext < (char *) edyn + dynamic_size - sizeof (* entry); ext++) { dynamic_nent++; @@ -8174,16 +8219,18 @@ get_64bit_dynamic_section (FILE * file) Elf64_External_Dyn * ext; Elf_Internal_Dyn * entry; + /* Read in the data. */ edyn = (Elf64_External_Dyn *) get_data (NULL, file, dynamic_addr, 1, dynamic_size, _("dynamic section")); if (!edyn) return 0; -/* SGI's ELF has more than one section in the DYNAMIC segment, and we - might not have the luxury of section headers. Look for the DT_NULL - terminator to determine the number of entries. */ + /* SGI's ELF has more than one section in the DYNAMIC segment, and we + might not have the luxury of section headers. Look for the DT_NULL + terminator to determine the number of entries. */ for (ext = edyn, dynamic_nent = 0; - (char *) ext < (char *) edyn + dynamic_size; + /* PR 17533 file: 033-67080-0.004 - do not read off the end of the buffer. */ + (char *) ext < ((char *) edyn) + dynamic_size - sizeof (* ext); ext++) { dynamic_nent++; @@ -8200,6 +8247,7 @@ get_64bit_dynamic_section (FILE * file) return 0; } + /* Convert from external to internal formats. */ for (ext = edyn, entry = dynamic_section; entry < dynamic_section + dynamic_nent; ext++, entry++) @@ -8300,6 +8348,7 @@ process_dynamic_section (FILE * file) section.sh_entsize = sizeof (Elf32_External_Sym); else section.sh_entsize = sizeof (Elf64_External_Sym); + section.sh_name = string_table_length; dynamic_symbols = GET_ELF_SYMBOLS (file, §ion, & num_dynamic_syms); if (num_dynamic_syms < 1) @@ -8372,7 +8421,7 @@ process_dynamic_section (FILE * file) /* PR binutils/17531: A corrupt file can trigger this test. So do not use an assert, instead generate an error message. */ if (sizeof (Elf_External_Syminfo) != entry->d_un.d_val) - error (_("Bad value (%d) for SYMINENT entry"), + error (_("Bad value (%d) for SYMINENT entry\n"), (int) entry->d_un.d_val); } else if (entry->d_tag == DT_SYMINSZ) @@ -8829,9 +8878,13 @@ process_dynamic_section (FILE * file) time_t atime = entry->d_un.d_val; tmp = gmtime (&atime); - printf ("%04u-%02u-%02uT%02u:%02u:%02u\n", - tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + /* PR 17533 file: 041-1244816-0.004. */ + if (tmp == NULL) + printf (_("<corrupt time val: %lx"), atime); + else + printf ("%04u-%02u-%02uT%02u:%02u:%02u\n", + tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); } break; @@ -9775,6 +9828,7 @@ process_symbol_table (FILE * file) bfd_vma * gnubuckets = NULL; bfd_vma * gnuchains = NULL; bfd_vma gnusymidx = 0; + bfd_size_type ngnuchains = 0; if (!do_syms && !do_dyn_syms && !do_histogram) return 1; @@ -9936,6 +9990,7 @@ process_symbol_table (FILE * file) } gnuchains = get_dynamic_data (file, maxchain, 4); + ngnuchains = maxchain; no_gnu_hash: if (gnuchains == NULL) @@ -9994,7 +10049,7 @@ process_symbol_table (FILE * file) print_dynamic_symbol (si, hn); si++; } - while ((gnuchains[off++] & 1) == 0); + while (off < ngnuchains && (gnuchains[off++] & 1) == 0); } } } @@ -10345,7 +10400,9 @@ process_symbol_table (FILE * file) bfd_vma off, length = 1; for (off = gnubuckets[hn] - gnusymidx; - (gnuchains[off] & 1) == 0; ++off) + /* PR 17531 file: 010-77222-0.004. */ + off < ngnuchains && (gnuchains[off] & 1) == 0; + ++off) ++length; lengths[hn] = length; if (length > maxlength) @@ -10515,7 +10572,7 @@ target_specific_reloc_handling (Elf_Internal_Rela * reloc, default: if (saved_sym != NULL) - error (_("Unhandled MSP430 reloc type found after SYM_DIFF reloc")); + error (_("Unhandled MSP430 reloc type found after SYM_DIFF reloc\n")); break; } break; @@ -10551,7 +10608,7 @@ target_specific_reloc_handling (Elf_Internal_Rela * reloc, break; default: if (saved_sym != NULL) - error (_("Unhandled MN10300 reloc type found after SYM_DIFF reloc")); + error (_("Unhandled MN10300 reloc type found after SYM_DIFF reloc\n")); break; } break; @@ -11254,15 +11311,18 @@ dump_section_as_strings (Elf_Internal_Shdr * section, FILE * file) if (data < end) { + size_t maxlen = end - data; + #ifndef __MSVCRT__ /* PR 11128: Use two separate invocations in order to work around bugs in the Solaris 8 implementation of printf. */ printf (" [%6tx] ", data - start); - printf ("%s\n", data); #else - printf (" [%6Ix] %s\n", (size_t) (data - start), data); + printf (" [%6Ix] ", (size_t) (data - start)); #endif - data += strlen (data); + print_symbol ((int) maxlen, data); + putchar ('\n'); + data += strnlen (data, maxlen); some_strings_shown = TRUE; } } @@ -11707,9 +11767,13 @@ display_tag_value (int tag, } else if (tag & 1) { - /* FIXME: we could read beyond END here. */ - printf ("\"%s\"\n", p); - p += strlen ((char *) p) + 1; + /* PR 17531 file: 027-19978-0.004. */ + size_t maxlen = (end - p) - 1; + + putchar ('"'); + print_symbol ((int) maxlen, (const char *) p); + printf ("\"\n"); + p += strnlen ((char *) p, maxlen) + 1; } else { @@ -11927,10 +11991,17 @@ display_arm_attribute (unsigned char * p, break; case 32: /* Tag_compatibility. */ - val = read_uleb128 (p, &len, end); - p += len; - printf (_("flag = %d, vendor = %s\n"), val, p); - p += strlen ((char *) p) + 1; + { + size_t maxlen; + + val = read_uleb128 (p, &len, end); + p += len; + maxlen = (end - p) - 1; + printf (_("flag = %d, vendor = "), val); + print_symbol ((int) maxlen, (const char *) p); + putchar ('\n'); + p += strnlen ((char *) p, maxlen) + 1; + } break; case 64: /* Tag_nodefaults. */ @@ -11945,14 +12016,15 @@ display_arm_attribute (unsigned char * p, { val = read_uleb128 (p, &len, end); p += len; - if ((unsigned int)val >= ARRAY_SIZE (arm_attr_tag_CPU_arch)) + if ((unsigned int) val >= ARRAY_SIZE (arm_attr_tag_CPU_arch)) printf ("??? (%d)\n", val); else printf ("%s\n", arm_attr_tag_CPU_arch[val]); } else printf ("???\n"); - while (*(p++) != '\0' /* NUL terminator. */); + while (p < end && *(p++) != '\0' /* NUL terminator. */) + ; break; default: @@ -11999,15 +12071,20 @@ display_gnu_attribute (unsigned char * p, { val = read_uleb128 (p, &len, end); p += len; + + printf (_("flag = %d, vendor = "), val); if (p == end) { - printf (_("flag = %d, vendor = <corrupt>\n"), val); + printf (_("<corrupt>\n")); warn (_("corrupt vendor attribute\n")); } else { - printf (_("flag = %d, vendor = %s\n"), val, p); - p += strlen ((char *) p) + 1; + size_t maxlen = (end - p) - 1; + + print_symbol ((int) maxlen, (const char *) p); + putchar ('\n'); + p += strnlen ((char *) p, maxlen) + 1; } return p; } @@ -12083,7 +12160,7 @@ display_power_gnu_attribute (unsigned char * p, { if (p == end) { - warn (_("corrupt Tag_GNU_Power_ABI_Struct_Return")); + warn (_("corrupt Tag_GNU_Power_ABI_Struct_Return\n")); return p; } @@ -12117,6 +12194,7 @@ display_sparc_hwcaps (int mask) if (mask) { int first = 1; + if (mask & ELF_SPARC_HWCAP_MUL32) fputs ("mul32", stdout), first = 0; if (mask & ELF_SPARC_HWCAP_DIV32) @@ -12151,8 +12229,8 @@ display_sparc_hwcaps (int mask) printf ("%scspare", first ? "" : "|"), first = 0; } else - fputc('0', stdout); - fputc('\n', stdout); + fputc ('0', stdout); + fputc ('\n', stdout); } static void @@ -12161,6 +12239,7 @@ display_sparc_hwcaps2 (int mask) if (mask) { int first = 1; + if (mask & ELF_SPARC_HWCAP2_FJATHPLUS) fputs ("fjathplus", stdout), first = 0; if (mask & ELF_SPARC_HWCAP2_VIS3B) @@ -12185,8 +12264,8 @@ display_sparc_hwcaps2 (int mask) printf ("%sfjaes", first ? "" : "|"), first = 0; } else - fputc('0', stdout); - fputc('\n', stdout); + fputc ('0', stdout); + fputc ('\n', stdout); } static unsigned char * @@ -12502,18 +12581,32 @@ display_tic6x_attribute (unsigned char * p, return p; case Tag_ABI_compatibility: - val = read_uleb128 (p, &len, end); - p += len; - printf (" Tag_ABI_compatibility: "); - printf (_("flag = %d, vendor = %s\n"), val, p); - p += strlen ((char *) p) + 1; - return p; + { + size_t maxlen; + + val = read_uleb128 (p, &len, end); + p += len; + printf (" Tag_ABI_compatibility: "); + maxlen = (end - p) - 1; + printf (_("flag = %d, vendor = "), val); + print_symbol ((int) maxlen, (const char *) p); + putchar ('\n'); + p += strnlen ((char *) p, maxlen) + 1; + return p; + } case Tag_ABI_conformance: - printf (" Tag_ABI_conformance: "); - printf ("\"%s\"\n", p); - p += strlen ((char *) p) + 1; - return p; + { + size_t maxlen; + + printf (" Tag_ABI_conformance: "); + maxlen = (end - p) - 1; + putchar ('"'); + print_symbol ((int) maxlen, (const char *) p); + printf ("\"\n"); + p += strnlen ((char *) p, maxlen) + 1; + return p; + } } return display_tag_value (tag, p, end); @@ -12622,8 +12715,13 @@ display_msp430x_attribute (unsigned char * p, if (tag & 1) { - printf ("\"%s\"\n", p); - p += strlen ((char *) p) + 1; + size_t maxlen; + + maxlen = (end - p) - 1; + putchar ('"'); + print_symbol ((int) maxlen, (const char *) p); + printf ("\"\n"); + p += strnlen ((char *) p, maxlen) + 1; } else { @@ -12645,11 +12743,6 @@ process_attributes (FILE * file, unsigned char * (* display_proc_gnu_attribute) (unsigned char *, int, const unsigned char * const)) { Elf_Internal_Shdr * sect; - unsigned char * contents; - unsigned char * p; - unsigned char * end; - bfd_vma section_len; - bfd_vma len; unsigned i; /* Find the section header so that we get the size. */ @@ -12657,6 +12750,9 @@ process_attributes (FILE * file, i < elf_header.e_shnum; i++, sect++) { + unsigned char * contents; + unsigned char * p; + if (sect->sh_type != proc_type && sect->sh_type != SHT_GNU_ATTRIBUTES) continue; @@ -12668,47 +12764,52 @@ process_attributes (FILE * file, p = contents; if (*p == 'A') { - len = sect->sh_size - 1; + bfd_vma section_len; + + section_len = sect->sh_size - 1; p++; - while (len > 0) + while (section_len > 0) { + bfd_vma attr_len; unsigned int namelen; bfd_boolean public_section; bfd_boolean gnu_section; - if (len <= 4) + if (section_len <= 4) { error (_("Tag section ends prematurely\n")); break; } - section_len = byte_get (p, 4); + attr_len = byte_get (p, 4); p += 4; - if (section_len > len) + if (attr_len > section_len) { - error (_("Length of attribute (%u) greater than length of section (%u)\n"), - (unsigned) section_len, (unsigned) len); - section_len = len; + error (_("Bad attribute length (%u > %u)\n"), + (unsigned) attr_len, (unsigned) section_len); + attr_len = section_len; } /* PR 17531: file: 001-101425-0.004 */ - else if (section_len < 5) + else if (attr_len < 5) { - error (_("Attribute length of %u is too small\n"), (unsigned) section_len); + error (_("Attribute length of %u is too small\n"), (unsigned) attr_len); break; } - - len -= section_len; - section_len -= 4; - namelen = strnlen ((char *) p, section_len) + 1; - if (namelen == 0 || namelen >= section_len) + section_len -= attr_len; + attr_len -= 4; + + namelen = strnlen ((char *) p, attr_len) + 1; + if (namelen == 0 || namelen >= attr_len) { error (_("Corrupt attribute section name\n")); break; } - printf (_("Attribute Section: %s\n"), p); + printf (_("Attribute Section: ")); + print_symbol (INT_MAX, (const char *) p); + putchar ('\n'); if (public_name && streq ((char *) p, public_name)) public_section = TRUE; @@ -12721,16 +12822,17 @@ process_attributes (FILE * file, gnu_section = FALSE; p += namelen; - section_len -= namelen; + attr_len -= namelen; - while (section_len > 0) + while (attr_len > 0 && p < contents + sect->sh_size) { int tag; int val; bfd_vma size; + unsigned char * end; /* PR binutils/17531: Safe handling of corrupt files. */ - if (section_len < 6) + if (attr_len < 6) { error (_("Unused bytes at end of section\n")); section_len = 0; @@ -12739,11 +12841,11 @@ process_attributes (FILE * file, tag = *(p++); size = byte_get (p, 4); - if (size > section_len) + if (size > attr_len) { error (_("Bad subsection length (%u > %u)\n"), - (unsigned) size, (unsigned) section_len); - size = section_len; + (unsigned) size, (unsigned) attr_len); + size = attr_len; } /* PR binutils/17531: Safe handling of corrupt files. */ if (size < 6) @@ -12754,8 +12856,9 @@ process_attributes (FILE * file, break; } - section_len -= size; + attr_len -= size; end = p + size - 1; + assert (end <= contents + sect->sh_size); p += 4; switch (tag) @@ -12787,24 +12890,28 @@ process_attributes (FILE * file, break; } - if (public_section) + if (public_section && display_pub_attribute != NULL) { while (p < end) p = display_pub_attribute (p, end); + assert (p <= end); } - else if (gnu_section) + else if (gnu_section && display_proc_gnu_attribute != NULL) { while (p < end) p = display_gnu_attribute (p, display_proc_gnu_attribute, end); + assert (p <= end); } - else + else if (p < end) { - printf (_(" Unknown section contexts\n")); + printf (_(" Unknown attribute:\n")); display_raw_attribute (p, end); p = end; } + else + attr_len = 0; } } } @@ -13093,7 +13200,10 @@ process_mips_specific (FILE * file) /* No information available. */ return 0; - for (entry = dynamic_section; entry->d_tag != DT_NULL; ++entry) + for (entry = dynamic_section; + /* PR 17531 file: 012-50589-0.004. */ + entry < dynamic_section + dynamic_nent && entry->d_tag != DT_NULL; + ++entry) switch (entry->d_tag) { case DT_MIPS_LIBLIST: @@ -13234,8 +13344,13 @@ process_mips_specific (FILE * file) sect = section_headers; /* Find the section header so that we get the size. */ - while (sect->sh_type != SHT_MIPS_OPTIONS) - ++sect; + sect = find_section_by_type (SHT_MIPS_OPTIONS); + /* PR 17533 file: 012-277276-0.004. */ + if (sect == NULL) + { + error (_("No MIPS_OPTIONS header found\n")); + return 0; + } eopt = (Elf_External_Options *) get_data (NULL, file, options_offset, 1, sect->sh_size, _("options")); |