From 591f7597d447d8d038d6d8e24a706d1d5e32eba1 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Mon, 1 Dec 2014 11:19:39 +0000 Subject: [PATCH] Add checks for memory access violations exposed by fuzzed archives. PR binutils/17531 * dwarf.c (process_cu_tu_index): Check for an out of range row index. * elfcomm.c (adjust_relative_path): Change name_len parameter to an unsigned long. Check for path length overflow. (process_archive_index_and_symbols): Check for invalid header size. (setup_archive): Add checks for invalid archives. (get_archive_member_name): Add range checks. * elfcomm.h (adjust_relative_path): Update prototyoe. * readelf.c (process_archive): Add range checks. --- binutils/ChangeLog | 14 ++++++++++++ binutils/dwarf.c | 8 +++++++ binutils/elfcomm.c | 55 +++++++++++++++++++++++++++++++++++++++++----- binutils/elfcomm.h | 2 +- binutils/readelf.c | 11 +++++----- 5 files changed, 79 insertions(+), 11 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index a0f688966f9..ecb7c8bf8e9 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,17 @@ +2014-12-01 Nick Clifton + + PR binutils/17531 + * dwarf.c (process_cu_tu_index): Check for an out of range row + index. + * elfcomm.c (adjust_relative_path): Change name_len parameter to + an unsigned long. Check for path length overflow. + (process_archive_index_and_symbols): Check for invalid header + size. + (setup_archive): Add checks for invalid archives. + (get_archive_member_name): Add range checks. + * elfcomm.h (adjust_relative_path): Update prototyoe. + * readelf.c (process_archive): Add range checks. + 2014-11-28 Alan Modra * readelf.c (get_32bit_elf_symbols): Cast bfd_size_type values to diff --git a/binutils/dwarf.c b/binutils/dwarf.c index e2bac1f5f80..5f953d51187 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -6796,6 +6796,14 @@ process_cu_tu_index (struct dwarf_section *section, int do_display) SAFE_BYTE_GET (row, pi, 4, limit); if (row != 0) { + /* PR 17531: file: a05f6ab3. */ + if (row >= nused) + { + warn (_("Row index (%u) is larger than number of used entries (%u)\n"), + row, nused); + return 0; + } + if (!do_display) memcpy (&this_set[row - 1].signature, ph, sizeof (uint64_t)); diff --git a/binutils/elfcomm.c b/binutils/elfcomm.c index f1502b9fdf6..bbf19550ece 100644 --- a/binutils/elfcomm.c +++ b/binutils/elfcomm.c @@ -386,10 +386,11 @@ byte_get_64 (unsigned char *field, elf_vma *high, elf_vma *low) char * adjust_relative_path (const char *file_name, const char *name, - int name_len) + unsigned long name_len) { char * member_file_name; const char * base_name = lbasename (file_name); + size_t amt; /* This is a proxy entry for a thin archive member. If the extended name table contains an absolute path @@ -399,7 +400,10 @@ adjust_relative_path (const char *file_name, const char *name, archive is located. */ if (IS_ABSOLUTE_PATH (name) || base_name == file_name) { - member_file_name = (char *) malloc (name_len + 1); + amt = name_len + 1; + if (amt == 0) + return NULL; + member_file_name = (char *) malloc (amt); if (member_file_name == NULL) { error (_("Out of memory\n")); @@ -413,7 +417,18 @@ adjust_relative_path (const char *file_name, const char *name, /* Concatenate the path components of the archive file name to the relative path name from the extended name table. */ size_t prefix_len = base_name - file_name; - member_file_name = (char *) malloc (prefix_len + name_len + 1); + + amt = prefix_len + name_len + 1; + /* PR 17531: file: 2896dc8b + Catch wraparound. */ + if (amt < prefix_len || amt < name_len) + { + error (_("Abnormal length of thin archive member name: %lx\n"), + name_len); + return NULL; + } + + member_file_name = (char *) malloc (amt); if (member_file_name == NULL) { error (_("Out of memory\n")); @@ -445,6 +460,14 @@ process_archive_index_and_symbols (struct archive_info * arch, unsigned long size; size = strtoul (arch->arhdr.ar_size, NULL, 10); + /* PR 17531: file: 912bd7de. */ + if ((signed long) size < 0) + { + error (_("%s: invalid archive header size: %ld\n"), + arch->file_name, size); + return FALSE; + } + size = size + (size & 1); arch->next_arhdr_offset += sizeof arch->arhdr + size; @@ -623,9 +646,17 @@ setup_archive (struct archive_info *arch, const char *file_name, { /* This is the archive string table holding long member names. */ arch->longnames_size = strtoul (arch->arhdr.ar_size, NULL, 10); + /* PR 17531: file: 01068045. */ + if (arch->longnames_size < 8) + { + error (_("%s: long name table is too small, (size = %ld)\n"), + file_name, arch->longnames_size); + return 1; + } arch->next_arhdr_offset += sizeof arch->arhdr + arch->longnames_size; - arch->longnames = (char *) malloc (arch->longnames_size); + /* Plus one to allow for a string terminator. */ + arch->longnames = (char *) malloc (arch->longnames_size + 1); if (arch->longnames == NULL) { error (_("Out of memory reading long symbol names in archive\n")); @@ -719,17 +750,31 @@ get_archive_member_name (struct archive_info *arch, if (arch->is_thin_archive && endp != NULL && * endp == ':') arch->nested_member_origin = strtoul (endp + 1, NULL, 10); + if (j > arch->longnames_size) + { + error (_("Found long name index (%ld) beyond end of long name table\n"),j); + return NULL; + } while ((j < arch->longnames_size) && (arch->longnames[j] != '\n') && (arch->longnames[j] != '\0')) j++; - if (arch->longnames[j-1] == '/') + if (j > 0 && arch->longnames[j-1] == '/') j--; + if (j > arch->longnames_size) + j = arch->longnames_size; arch->longnames[j] = '\0'; if (!arch->is_thin_archive || arch->nested_member_origin == 0) return arch->longnames + k; + /* PR 17531: file: 2896dc8b. */ + if (k >= j) + { + error (_("Invalid Thin archive member name\n")); + return NULL; + } + /* This is a proxy for a member of a nested archive. Find the name of the member in that archive. */ member_file_name = adjust_relative_path (arch->file_name, diff --git a/binutils/elfcomm.h b/binutils/elfcomm.h index f41a8aca029..4fd2d6c97b8 100644 --- a/binutils/elfcomm.h +++ b/binutils/elfcomm.h @@ -77,7 +77,7 @@ struct archive_info }; /* Return the path name for a proxy entry in a thin archive. */ -extern char *adjust_relative_path (const char *, const char *, int); +extern char *adjust_relative_path (const char *, const char *, unsigned long); /* Read the symbol table and long-name table from an archive. */ extern int setup_archive (struct archive_info *, const char *, FILE *, diff --git a/binutils/readelf.c b/binutils/readelf.c index a6d563fbf5e..4e16bd62bc9 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -15261,11 +15261,11 @@ process_archive (char * file_name, FILE * file, bfd_boolean is_thin_archive) error (_("%s: unable to dump the index as none was found\n"), file_name); else { - unsigned int i, l; + unsigned long i, l; unsigned long current_pos; - printf (_("Index of archive %s: (%ld entries, 0x%lx bytes in the symbol table)\n"), - file_name, (long) arch.index_num, arch.sym_size); + printf (_("Index of archive %s: (%lu entries, 0x%lx bytes in the symbol table)\n"), + file_name, (unsigned long) arch.index_num, arch.sym_size); current_pos = ftell (file); for (i = l = 0; i < arch.index_num; i++) @@ -15296,8 +15296,9 @@ process_archive (char * file_name, FILE * file, bfd_boolean is_thin_archive) file_name); break; } - printf ("\t%s\n", arch.sym_table + l); - l += strlen (arch.sym_table + l) + 1; + /* PR 17531: file: 0b6630b2. */ + printf ("\t%.*s\n", (int) (arch.sym_size - l), arch.sym_table + l); + l += strnlen (arch.sym_table + l, arch.sym_size - l) + 1; } if (arch.uses_64bit_indicies) -- 2.30.2