PR binutils/11889

* readelf.c (get_32bit_elf_symbols): Check for a corrupt
        sh_entsize.
        (get_64bit_elf_symbols): Likewise.
        (process_symbol_table): Likewise.
        (process_section_groups): Check for corrupt headers.
        (process_version_sections): Check for corrupt indicies.
        (process_corefile_note_segment): Likewise.
This commit is contained in:
Nick Clifton
2010-08-13 16:02:16 +00:00
parent 8dedea0203
commit dd24e3da30
2 changed files with 130 additions and 31 deletions

View File

@ -1,3 +1,14 @@
2010-08-13 Dan Rosenberg <dan.j.rosenberg@gmail.com>
PR binutils/11889
* readelf.c (get_32bit_elf_symbols): Check for a corrupt
sh_entsize.
(get_64bit_elf_symbols): Likewise.
(process_symbol_table): Likewise.
(process_section_groups): Check for corrupt headers.
(process_version_sections): Check for corrupt indicies.
(process_corefile_note_segment): Likewise.
2010-08-13 Nathan Sidwell <nathan@codesourcery.com> 2010-08-13 Nathan Sidwell <nathan@codesourcery.com>
* readelf.c (get_machine_flags): Detect CF ISA C and EMAC_B * readelf.c (get_machine_flags): Detect CF ISA C and EMAC_B

View File

@ -4046,15 +4046,30 @@ static Elf_Internal_Sym *
get_32bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section) get_32bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section)
{ {
unsigned long number; unsigned long number;
Elf32_External_Sym * esyms; Elf32_External_Sym * esyms = NULL;
Elf_External_Sym_Shndx * shndx; Elf_External_Sym_Shndx * shndx;
Elf_Internal_Sym * isyms; Elf_Internal_Sym * isyms = NULL;
Elf_Internal_Sym * psym; Elf_Internal_Sym * psym;
unsigned int j; unsigned int j;
/* Run some sanity checks first. */
if (section->sh_entsize == 0)
{
error (_("sh_entsize is zero\n"));
return NULL;
}
number = section->sh_size / section->sh_entsize;
if (number * sizeof (Elf32_External_Sym) > section->sh_size + 1)
{
error (_("Invalid sh_entsize\n"));
return NULL;
}
esyms = (Elf32_External_Sym *) get_data (NULL, file, section->sh_offset, 1, esyms = (Elf32_External_Sym *) get_data (NULL, file, section->sh_offset, 1,
section->sh_size, _("symbols")); section->sh_size, _("symbols"));
if (!esyms) if (esyms == NULL)
return NULL; return NULL;
shndx = NULL; shndx = NULL;
@ -4066,28 +4081,19 @@ get_32bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section)
symtab_shndx_hdr->sh_offset, symtab_shndx_hdr->sh_offset,
1, symtab_shndx_hdr->sh_size, 1, symtab_shndx_hdr->sh_size,
_("symtab shndx")); _("symtab shndx"));
if (!shndx) if (shndx == NULL)
{ goto exit_point;
free (esyms);
return NULL;
}
} }
number = section->sh_size / section->sh_entsize;
isyms = (Elf_Internal_Sym *) cmalloc (number, sizeof (Elf_Internal_Sym)); isyms = (Elf_Internal_Sym *) cmalloc (number, sizeof (Elf_Internal_Sym));
if (isyms == NULL) if (isyms == NULL)
{ {
error (_("Out of memory\n")); error (_("Out of memory\n"));
if (shndx) goto exit_point;
free (shndx);
free (esyms);
return NULL;
} }
for (j = 0, psym = isyms; for (j = 0, psym = isyms; j < number; j++, psym++)
j < number;
j++, psym++)
{ {
psym->st_name = BYTE_GET (esyms[j].st_name); psym->st_name = BYTE_GET (esyms[j].st_name);
psym->st_value = BYTE_GET (esyms[j].st_value); psym->st_value = BYTE_GET (esyms[j].st_value);
@ -4102,8 +4108,10 @@ get_32bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section)
psym->st_other = BYTE_GET (esyms[j].st_other); psym->st_other = BYTE_GET (esyms[j].st_other);
} }
exit_point:
if (shndx) if (shndx)
free (shndx); free (shndx);
if (esyms)
free (esyms); free (esyms);
return isyms; return isyms;
@ -4119,6 +4127,21 @@ get_64bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section)
Elf_Internal_Sym * psym; Elf_Internal_Sym * psym;
unsigned int j; unsigned int j;
/* Run some sanity checks first. */
if (section->sh_entsize == 0)
{
error (_("sh_entsize is zero\n"));
return NULL;
}
number = section->sh_size / section->sh_entsize;
if (number * sizeof (Elf64_External_Sym) > section->sh_size + 1)
{
error (_("Invalid sh_entsize\n"));
return NULL;
}
esyms = (Elf64_External_Sym *) get_data (NULL, file, section->sh_offset, 1, esyms = (Elf64_External_Sym *) get_data (NULL, file, section->sh_offset, 1,
section->sh_size, _("symbols")); section->sh_size, _("symbols"));
if (!esyms) if (!esyms)
@ -4140,7 +4163,6 @@ get_64bit_elf_symbols (FILE * file, Elf_Internal_Shdr * section)
} }
} }
number = section->sh_size / section->sh_entsize;
isyms = (Elf_Internal_Sym *) cmalloc (number, sizeof (Elf_Internal_Sym)); isyms = (Elf_Internal_Sym *) cmalloc (number, sizeof (Elf_Internal_Sym));
if (isyms == NULL) if (isyms == NULL)
@ -4965,6 +4987,12 @@ process_section_groups (FILE * file)
symtab = GET_ELF_SYMBOLS (file, symtab_sec); symtab = GET_ELF_SYMBOLS (file, symtab_sec);
} }
if (symtab == NULL)
{
error (_("Corrupt header in group section `%s'\n"), name);
continue;
}
sym = symtab + section->sh_info; sym = symtab + section->sh_info;
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
@ -6440,7 +6468,6 @@ decode_arm_unwind (struct arm_unw_aux_info *aux,
} }
else else
{ {
per_index = (word >> 24) & 0x7f; per_index = (word >> 24) & 0x7f;
if (per_index != 0 && per_index != 1 && per_index != 2) if (per_index != 0 && per_index != 1 && per_index != 2)
{ {
@ -7773,6 +7800,10 @@ process_version_sections (FILE * file)
int j; int j;
int isum; int isum;
/* Check for negative or very large indicies. */
if ((unsigned char *) edefs + idx < (unsigned char *) edefs)
break;
vstart = ((char *) edefs) + idx; vstart = ((char *) edefs) + idx;
if (vstart + sizeof (*edef) > endbuf) if (vstart + sizeof (*edef) > endbuf)
break; break;
@ -7793,6 +7824,11 @@ process_version_sections (FILE * file)
printf (_(" Index: %d Cnt: %d "), printf (_(" Index: %d Cnt: %d "),
ent.vd_ndx, ent.vd_cnt); ent.vd_ndx, ent.vd_cnt);
/* Check for overflow. */
if ((unsigned char *)(vstart + ent.vd_aux) < (unsigned char *) vstart
|| (unsigned char *)(vstart + ent.vd_aux) > (unsigned char *) endbuf)
break;
vstart += ent.vd_aux; vstart += ent.vd_aux;
eaux = (Elf_External_Verdaux *) vstart; eaux = (Elf_External_Verdaux *) vstart;
@ -7809,6 +7845,11 @@ process_version_sections (FILE * file)
for (j = 1; j < ent.vd_cnt; j++) for (j = 1; j < ent.vd_cnt; j++)
{ {
/* Check for overflow. */
if ((unsigned char *)(vstart + aux.vda_next) < (unsigned char *) vstart
|| (unsigned char *)(vstart + aux.vda_next) > (unsigned char *) endbuf)
break;
isum += aux.vda_next; isum += aux.vda_next;
vstart += aux.vda_next; vstart += aux.vda_next;
@ -7826,11 +7867,13 @@ process_version_sections (FILE * file)
printf (_(" %#06x: Parent %d, name index: %ld\n"), printf (_(" %#06x: Parent %d, name index: %ld\n"),
isum, j, aux.vda_name); isum, j, aux.vda_name);
} }
if (j < ent.vd_cnt) if (j < ent.vd_cnt)
printf (_(" Version def aux past end of section\n")); printf (_(" Version def aux past end of section\n"));
idx += ent.vd_next; idx += ent.vd_next;
} }
if (cnt < section->sh_info) if (cnt < section->sh_info)
printf (_(" Version definition past end of section\n")); printf (_(" Version definition past end of section\n"));
@ -7874,6 +7917,9 @@ process_version_sections (FILE * file)
int isum; int isum;
char * vstart; char * vstart;
if ((unsigned char *) eneed + idx < (unsigned char *) eneed)
break;
vstart = ((char *) eneed) + idx; vstart = ((char *) eneed) + idx;
if (vstart + sizeof (*entry) > endbuf) if (vstart + sizeof (*entry) > endbuf)
break; break;
@ -7895,6 +7941,11 @@ process_version_sections (FILE * file)
printf (_(" Cnt: %d\n"), ent.vn_cnt); printf (_(" Cnt: %d\n"), ent.vn_cnt);
/* Check for overflow. */
if ((unsigned char *)(vstart + ent.vn_aux) < (unsigned char *) vstart
|| (unsigned char *)(vstart + ent.vn_aux) > (unsigned char *) endbuf)
break;
vstart += ent.vn_aux; vstart += ent.vn_aux;
for (j = 0, isum = idx + ent.vn_aux; j < ent.vn_cnt; ++j) for (j = 0, isum = idx + ent.vn_aux; j < ent.vn_cnt; ++j)
@ -7922,6 +7973,11 @@ process_version_sections (FILE * file)
printf (_(" Flags: %s Version: %d\n"), printf (_(" Flags: %s Version: %d\n"),
get_ver_flags (aux.vna_flags), aux.vna_other); get_ver_flags (aux.vna_flags), aux.vna_other);
/* Check for overflow. */
if ((unsigned char *)(vstart + aux.vna_next) < (unsigned char *) vstart
|| (unsigned char *)(vstart + aux.vna_next) > (unsigned char *) endbuf)
break;
isum += aux.vna_next; isum += aux.vna_next;
vstart += aux.vna_next; vstart += aux.vna_next;
} }
@ -7961,6 +8017,8 @@ process_version_sections (FILE * file)
found = 1; found = 1;
symbols = GET_ELF_SYMBOLS (file, link_section); symbols = GET_ELF_SYMBOLS (file, link_section);
if (symbols == NULL)
break;
string_sec = section_headers + link_section->sh_link; string_sec = section_headers + link_section->sh_link;
@ -8022,6 +8080,16 @@ process_version_sections (FILE * file)
nn = printf ("%4x%c", data[cnt + j] & VERSYM_VERSION, nn = printf ("%4x%c", data[cnt + j] & VERSYM_VERSION,
data[cnt + j] & VERSYM_HIDDEN ? 'h' : ' '); data[cnt + j] & VERSYM_HIDDEN ? 'h' : ' ');
/* If this index value is greater than the size of the symbols
array, break to avoid an out-of-bounds read, */
if ((unsigned long)(cnt + j) >=
((unsigned long)link_section->sh_size /
(unsigned long)link_section->sh_entsize))
{
warn (_("invalid index into symbol array\n"));
break;
}
check_def = 1; check_def = 1;
check_need = 1; check_need = 1;
if (symbols[cnt + j].st_shndx >= elf_header.e_shnum if (symbols[cnt + j].st_shndx >= elf_header.e_shnum
@ -8729,9 +8797,17 @@ process_symbol_table (FILE * file)
&& section->sh_type == SHT_SYMTAB)) && section->sh_type == SHT_SYMTAB))
continue; continue;
if (section->sh_entsize == 0)
{
printf (_("\nSymbol table '%s' has a sh_entsize of zero!\n"),
SECTION_NAME (section));
continue;
}
printf (_("\nSymbol table '%s' contains %lu entries:\n"), printf (_("\nSymbol table '%s' contains %lu entries:\n"),
SECTION_NAME (section), SECTION_NAME (section),
(unsigned long) (section->sh_size / section->sh_entsize)); (unsigned long) (section->sh_size / section->sh_entsize));
if (is_32bit_elf) if (is_32bit_elf)
printf (_(" Num: Value Size Type Bind Vis Ndx Name\n")); printf (_(" Num: Value Size Type Bind Vis Ndx Name\n"));
else else
@ -11825,7 +11901,7 @@ process_corefile_note_segment (FILE * file, bfd_vma offset, bfd_vma length)
pnotes = (Elf_External_Note *) get_data (NULL, file, offset, 1, length, pnotes = (Elf_External_Note *) get_data (NULL, file, offset, 1, length,
_("notes")); _("notes"));
if (!pnotes) if (pnotes == NULL)
return 0; return 0;
external = pnotes; external = pnotes;
@ -11849,7 +11925,8 @@ process_corefile_note_segment (FILE * file, bfd_vma offset, bfd_vma length)
next = (Elf_External_Note *) (inote.descdata + align_power (inote.descsz, 2)); next = (Elf_External_Note *) (inote.descdata + align_power (inote.descsz, 2));
if (((char *) next) > (((char *) pnotes) + length)) if ( ((char *) next > ((char *) pnotes) + length)
|| ((char *) next < (char *) pnotes))
{ {
warn (_("corrupt note found at offset %lx into core notes\n"), warn (_("corrupt note found at offset %lx into core notes\n"),
(unsigned long) ((char *) external - (char *) pnotes)); (unsigned long) ((char *) external - (char *) pnotes));
@ -11860,6 +11937,17 @@ process_corefile_note_segment (FILE * file, bfd_vma offset, bfd_vma length)
external = next; external = next;
/* Prevent out-of-bounds indexing. */
if (inote.namedata + inote.namesz >= (char *) pnotes + length
|| inote.namedata + inote.namesz < inote.namedata)
{
warn (_("corrupt note found at offset %lx into core notes\n"),
(unsigned long) ((char *) external - (char *) pnotes));
warn (_(" type: %lx, namesize: %08lx, descsize: %08lx\n"),
inote.type, inote.namesz, inote.descsz);
break;
}
/* Verify that name is null terminated. It appears that at least /* Verify that name is null terminated. It appears that at least
one version of Linux (RedHat 6.0) generates corefiles that don't one version of Linux (RedHat 6.0) generates corefiles that don't
comply with the ELF spec by failing to include the null byte in comply with the ELF spec by failing to include the null byte in