From 896ca0981329171639b1fe0b934393a79ef4fdfb Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Mon, 5 Jan 2015 23:13:50 +0000 Subject: [PATCH] More fixes for invalid memory accesses triggered by fuzzed binaries. PR binutils/17512 * nm.c (print_symbol): Add 'is_synthetic' parameter. Use it to help initialize the info.elfinfo field. (print_size_symbols): Add 'synth_count' parameter. Use it to set the is_synthetic parameter when calling print_symbol. (print_symbols): Likewise. (display_rel_file): Pass synth_count to printing function. (display_archive): Break loop if the last archive displayed matches the current archive. * size.c (display_archive): Likewise. * archive.c (do_slurp_bsd_armap): Make sure that the parsed sized is at least big enough for the header to be read. * elf32-i386.c (elf_i386_get_plt_sym_val): Skip unknown relocs. * mach-o.c (bfd_mach_o_get_synthetic_symtab): Add range checks. (bfd_mach_o_read_command): Prevetn duplicate error messages about unrecognized commands. * syms.c (_bfd_stab_section_find_nearest_line): Add range checks when indexing into the string table. --- bfd/ChangeLog | 12 ++++++++ bfd/archive.c | 3 +- bfd/elf32-i386.c | 5 ++-- bfd/elfcode.h | 3 +- bfd/mach-o.c | 61 +++++++++++++++++++++++++++++++-------- bfd/syms.c | 12 ++++++++ binutils/ChangeLog | 13 +++++++++ binutils/nm.c | 71 ++++++++++++++++++++++++++++++++-------------- binutils/size.c | 9 +++++- 9 files changed, 150 insertions(+), 39 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 541c3294c9a..0545a7ef0b9 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,15 @@ +2015-01-05 Nick Clifton + + PR binutils/17512 + * archive.c (do_slurp_bsd_armap): Make sure that the parsed sized + is at least big enough for the header to be read. + * elf32-i386.c (elf_i386_get_plt_sym_val): Skip unknown relocs. + * mach-o.c (bfd_mach_o_get_synthetic_symtab): Add range checks. + (bfd_mach_o_read_command): Prevetn duplicate error messages about + unrecognized commands. + * syms.c (_bfd_stab_section_find_nearest_line): Add range checks + when indexing into the string table. + 2015-01-01 Alan Modra Update year range in copyright notice of all files. diff --git a/bfd/archive.c b/bfd/archive.c index dc5f76c83f5..cc4c52f7be3 100644 --- a/bfd/archive.c +++ b/bfd/archive.c @@ -903,7 +903,8 @@ do_slurp_bsd_armap (bfd *abfd) parsed_size = mapdata->parsed_size; free (mapdata); /* PR 17512: file: 883ff754. */ - if (parsed_size == 0) + /* PR 17512: file: 0458885f. */ + if (parsed_size < 4) return FALSE; raw_armap = (bfd_byte *) bfd_zalloc (abfd, parsed_size); diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index 24e3d4cc319..85acf0fce6d 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -5194,8 +5194,9 @@ bad_return: { long reloc_index; - if (p->howto->type != R_386_JUMP_SLOT - && p->howto->type != R_386_IRELATIVE) + if (p->howto == NULL /* PR 17512: file: bc9d6cf5. */ + || (p->howto->type != R_386_JUMP_SLOT + && p->howto->type != R_386_IRELATIVE)) continue; reloc_index = H_GET_32 (abfd, (plt_contents + plt_offset diff --git a/bfd/elfcode.h b/bfd/elfcode.h index 481b007232e..1a9d3045124 100644 --- a/bfd/elfcode.h +++ b/bfd/elfcode.h @@ -1214,10 +1214,9 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic) for (isym = isymbuf + 1, sym = symbase; isym < isymend; isym++, sym++) { memcpy (&sym->internal_elf_sym, isym, sizeof (Elf_Internal_Sym)); - sym->symbol.the_bfd = abfd; + sym->symbol.the_bfd = abfd; sym->symbol.name = bfd_elf_sym_name (abfd, hdr, isym, NULL); - sym->symbol.value = isym->st_value; if (isym->st_shndx == SHN_UNDEF) diff --git a/bfd/mach-o.c b/bfd/mach-o.c index 14d6276c348..5dd6250f9d6 100644 --- a/bfd/mach-o.c +++ b/bfd/mach-o.c @@ -790,18 +790,19 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd, bfd_mach_o_dysymtab_command *dysymtab = mdata->dysymtab; bfd_mach_o_symtab_command *symtab = mdata->symtab; asymbol *s; + char * s_start; + char * s_end; unsigned long count, i, j, n; size_t size; char *names; char *nul_name; + const char stub [] = "$stub"; *ret = NULL; /* Stop now if no symbols or no indirect symbols. */ - if (dysymtab == NULL || symtab == NULL || symtab->symbols == NULL) - return 0; - - if (dysymtab->nindirectsyms == 0) + if (dysymtab == NULL || dysymtab->nindirectsyms == 0 + || symtab == NULL || symtab->symbols == NULL) return 0; /* We need to allocate a bfd symbol for every indirect symbol and to @@ -811,19 +812,23 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd, for (j = 0; j < count; j++) { + const char * strng; unsigned int isym = dysymtab->indirect_syms[j]; /* Some indirect symbols are anonymous. */ - if (isym < symtab->nsyms && symtab->symbols[isym].symbol.name) - size += strlen (symtab->symbols[isym].symbol.name) + sizeof ("$stub"); + if (isym < symtab->nsyms && (strng = symtab->symbols[isym].symbol.name)) + /* PR 17512: file: f5b8eeba. */ + size += strnlen (strng, symtab->strsize - (strng - symtab->strtab)) + sizeof (stub); } - s = *ret = (asymbol *) bfd_malloc (size); + s_start = bfd_malloc (size); + s = *ret = (asymbol *) s_start; if (s == NULL) return -1; names = (char *) (s + count); nul_name = names; *names++ = 0; + s_end = s_start + size; n = 0; for (i = 0; i < mdata->nsects; i++) @@ -843,10 +848,19 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd, last = first + bfd_mach_o_section_get_nbr_indirect (abfd, sec); addr = sec->addr; entry_size = bfd_mach_o_section_get_entry_size (abfd, sec); + + /* PR 17512: file: 08e15eec. */ + if (first >= count || last >= count || first > last) + goto fail; + for (j = first; j < last; j++) { unsigned int isym = dysymtab->indirect_syms[j]; + /* PR 17512: file: 04d64d9b. */ + if (((char *) s) + sizeof (* s) > s_end) + goto fail; + s->flags = BSF_GLOBAL | BSF_SYNTHETIC; s->section = sec->bfdsection; s->value = addr - sec->addr; @@ -860,10 +874,16 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd, s->name = names; len = strlen (sym); + /* PR 17512: file: 47dfd4d2. */ + if (names + len >= s_end) + goto fail; memcpy (names, sym, len); names += len; - memcpy (names, "$stub", sizeof ("$stub")); - names += sizeof ("$stub"); + /* PR 17512: file: 18f340a4. */ + if (names + sizeof (stub) >= s_end) + goto fail; + memcpy (names, stub, sizeof (stub)); + names += sizeof (stub); } else s->name = nul_name; @@ -879,6 +899,11 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd, } return n; + + fail: + free (s_start); + * ret = NULL; + return -1; } void @@ -4660,9 +4685,21 @@ bfd_mach_o_read_command (bfd *abfd, bfd_mach_o_load_command *command) return FALSE; break; default: - (*_bfd_error_handler)(_("%B: unknown load command 0x%lx"), - abfd, (unsigned long) command->type); - break; + { + static bfd_boolean unknown_set = FALSE; + static unsigned long unknown_command = 0; + + /* Prevent reams of error messages when parsing corrupt binaries. */ + if (!unknown_set) + unknown_set = TRUE; + else if (command->type == unknown_command) + break; + unknown_command = command->type; + + (*_bfd_error_handler)(_("%B: unknown load command 0x%lx"), + abfd, (unsigned long) command->type); + break; + } } return TRUE; diff --git a/bfd/syms.c b/bfd/syms.c index 37be5f28275..0b0d26ded60 100644 --- a/bfd/syms.c +++ b/bfd/syms.c @@ -823,6 +823,7 @@ _bfd_generic_read_minisymbols (bfd *abfd, *minisymsp = syms; *sizep = sizeof (asymbol *); + return symcount; error_return: @@ -1191,6 +1192,8 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, { nul_fun = stab; nul_str = str; + if (file_name >= (char *) info->strs + strsize) + file_name = NULL; if (stab + STABSIZE + TYPEOFF < info->stabs + stabsize && *(stab + STABSIZE + TYPEOFF) == (bfd_byte) N_SO) { @@ -1200,6 +1203,8 @@ _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) + file_name = NULL; } } break; @@ -1207,6 +1212,9 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, case N_SOL: /* 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) + file_name = NULL; break; case N_FUN: @@ -1214,6 +1222,8 @@ _bfd_stab_section_find_nearest_line (bfd *abfd, function_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF); if (function_name == (char *) str) continue; + if (function_name >= (char *) info->strs + strsize) + function_name = NULL; nul_fun = NULL; info->indextable[i].val = bfd_get_32 (abfd, stab + VALOFF); @@ -1321,6 +1331,8 @@ _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) + file_name = NULL; *pline = 0; } break; diff --git a/binutils/ChangeLog b/binutils/ChangeLog index ab35da9139b..a4359839615 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,16 @@ +2015-01-05 Nick Clifton + + PR binutils/17512 + * nm.c (print_symbol): Add 'is_synthetic' parameter. Use it to + help initialize the info.elfinfo field. + (print_size_symbols): Add 'synth_count' parameter. Use it to set + the is_synthetic parameter when calling print_symbol. + (print_symbols): Likewise. + (display_rel_file): Pass synth_count to printing function. + (display_archive): Break loop if the last archive displayed + matches the current archive. + * size.c (display_archive): Likewise. + 2015-01-05 Nick Clifton PR binutils/17531 diff --git a/binutils/nm.c b/binutils/nm.c index bdc6078adaa..76013df45b0 100644 --- a/binutils/nm.c +++ b/binutils/nm.c @@ -806,7 +806,11 @@ get_relocs (bfd *abfd, asection *sec, void *dataarg) /* Print a single symbol. */ static void -print_symbol (bfd *abfd, asymbol *sym, bfd_vma ssize, bfd *archive_bfd) +print_symbol (bfd * abfd, + asymbol * sym, + bfd_vma ssize, + bfd * archive_bfd, + bfd_boolean is_synthetic) { symbol_info syminfo; struct extended_symbol_info info; @@ -816,12 +820,12 @@ print_symbol (bfd *abfd, asymbol *sym, bfd_vma ssize, bfd *archive_bfd) format->print_symbol_filename (archive_bfd, abfd); bfd_get_symbol_info (abfd, sym, &syminfo); + info.sinfo = &syminfo; info.ssize = ssize; - if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) - info.elfinfo = (elf_symbol_type *) sym; - else - info.elfinfo = NULL; + /* Synthetic symbols do not have a full elf_symbol_type set of data available. */ + info.elfinfo = is_synthetic ? NULL : elf_symbol_from (abfd, sym); + format->print_symbol_info (&info, abfd); if (line_numbers) @@ -941,12 +945,17 @@ print_symbol (bfd *abfd, asymbol *sym, bfd_vma ssize, bfd *archive_bfd) /* Print the symbols when sorting by size. */ static void -print_size_symbols (bfd *abfd, bfd_boolean is_dynamic, - struct size_sym *symsizes, long symcount, - bfd *archive_bfd) +print_size_symbols (bfd * abfd, + bfd_boolean is_dynamic, + struct size_sym * symsizes, + long symcount, + long synth_count, + bfd * archive_bfd) { asymbol *store; - struct size_sym *from, *fromend; + struct size_sym *from; + struct size_sym *fromend; + struct size_sym *fromsynth; store = bfd_make_empty_symbol (abfd); if (store == NULL) @@ -954,6 +963,8 @@ print_size_symbols (bfd *abfd, bfd_boolean is_dynamic, from = symsizes; fromend = from + symcount; + fromsynth = symsizes + (symcount - synth_count); + for (; from < fromend; from++) { asymbol *sym; @@ -962,20 +973,34 @@ print_size_symbols (bfd *abfd, bfd_boolean is_dynamic, if (sym == NULL) bfd_fatal (bfd_get_filename (abfd)); - print_symbol (abfd, sym, from->size, archive_bfd); + print_symbol (abfd, sym, from->size, archive_bfd, from >= fromsynth); } } -/* Print the symbols. If ARCHIVE_BFD is non-NULL, it is the archive - containing ABFD. */ +/* Print the symbols of ABFD that are held in MINISYMS. + + If ARCHIVE_BFD is non-NULL, it is the archive containing ABFD. + + SYMCOUNT is the number of symbols in MINISYMS and SYNTH_COUNT + is the number of these that are synthetic. Synthetic symbols, + if any are present, always come at the end of the MINISYMS. + + SIZE is the size of a symbol in MINISYMS. */ static void -print_symbols (bfd *abfd, bfd_boolean is_dynamic, void *minisyms, long symcount, - unsigned int size, bfd *archive_bfd) +print_symbols (bfd * abfd, + bfd_boolean is_dynamic, + void * minisyms, + long symcount, + long synth_count, + unsigned int size, + bfd * archive_bfd) { asymbol *store; - bfd_byte *from, *fromend; + bfd_byte *from; + bfd_byte *fromend; + bfd_byte *fromsynth; store = bfd_make_empty_symbol (abfd); if (store == NULL) @@ -983,6 +1008,8 @@ print_symbols (bfd *abfd, bfd_boolean is_dynamic, void *minisyms, long symcount, from = (bfd_byte *) minisyms; fromend = from + symcount * size; + fromsynth = (bfd_byte *) minisyms + ((symcount - synth_count) * size); + for (; from < fromend; from += size) { asymbol *sym; @@ -991,7 +1018,7 @@ print_symbols (bfd *abfd, bfd_boolean is_dynamic, void *minisyms, long symcount, if (sym == NULL) bfd_fatal (bfd_get_filename (abfd)); - print_symbol (abfd, sym, (bfd_vma) 0, archive_bfd); + print_symbol (abfd, sym, (bfd_vma) 0, archive_bfd, from >= fromsynth); } } @@ -1001,6 +1028,7 @@ static void display_rel_file (bfd *abfd, bfd *archive_bfd) { long symcount; + long synth_count = 0; void *minisyms; unsigned int size; struct size_sym *symsizes; @@ -1031,11 +1059,10 @@ display_rel_file (bfd *abfd, bfd *archive_bfd) non_fatal (_("%s: no symbols"), bfd_get_filename (abfd)); return; } - + if (show_synthetic && size == sizeof (asymbol *)) { asymbol *synthsyms; - long synth_count; asymbol **static_syms = NULL; asymbol **dyn_syms = NULL; long static_count = 0; @@ -1061,6 +1088,7 @@ display_rel_file (bfd *abfd, bfd *archive_bfd) bfd_fatal (bfd_get_filename (abfd)); } } + synth_count = bfd_get_synthetic_symtab (abfd, static_count, static_syms, dyn_count, dyn_syms, &synthsyms); if (synth_count > 0) @@ -1106,9 +1134,9 @@ display_rel_file (bfd *abfd, bfd *archive_bfd) } if (! sort_by_size) - print_symbols (abfd, dynamic, minisyms, symcount, size, archive_bfd); + print_symbols (abfd, dynamic, minisyms, symcount, synth_count, size, archive_bfd); else - print_size_symbols (abfd, dynamic, symsizes, symcount, archive_bfd); + print_size_symbols (abfd, dynamic, symsizes, symcount, synth_count, archive_bfd); free (minisyms); free (symsizes); @@ -1181,6 +1209,8 @@ display_archive (bfd *file) bfd_close (last_arfile); lineno_cache_bfd = NULL; lineno_cache_rel_bfd = NULL; + if (arfile == last_arfile) + return; } last_arfile = arfile; } @@ -1434,7 +1464,6 @@ print_symbol_info_bsd (struct extended_symbol_info *info, bfd *abfd) print_value (abfd, SYM_SIZE (info)); else print_value (abfd, SYM_VALUE (info)); - if (print_size && SYM_SIZE (info)) { printf (" "); diff --git a/binutils/size.c b/binutils/size.c index 1035f2be2aa..465603f243d 100644 --- a/binutils/size.c +++ b/binutils/size.c @@ -365,7 +365,14 @@ display_archive (bfd *file) display_bfd (arfile); if (last_arfile != NULL) - bfd_close (last_arfile); + { + bfd_close (last_arfile); + + /* PR 17512: file: a244edbc. */ + if (last_arfile == arfile) + return; + } + last_arfile = arfile; } -- 2.30.2