readelf: memory leaks in process_dynamic_section

This fixes some code that assumed only one PT_LOAD would contain
DT_SYMTAB.  Which is normally the case, but fuzzers thoroughly mess
with object files.

	* readelf.c (get_num_dynamic_syms): Check for nbuckets and nchains
	non-zero.
	(process_dynamic_section): Call get_num_dynamic_syms once rather
	than in segment loop.  Break out of segment loop on a successful
	load of dynamic symbols.  Formatting.
	(process_object): Return error status from process_dynamic_section.
This commit is contained in:
Alan Modra 2020-04-24 09:51:38 +09:30
parent 5e5bbc7e79
commit 2482f30615
2 changed files with 82 additions and 66 deletions

View File

@ -1,3 +1,12 @@
2020-04-24 Alan Modra <amodra@gmail.com>
* readelf.c (get_num_dynamic_syms): Check for nbuckets and nchains
non-zero.
(process_dynamic_section): Call get_num_dynamic_syms once rather
than in segment loop. Break out of segment loop on a successful
load of dynamic symbols. Formatting.
(process_object): Return error status from process_dynamic_section.
2020-04-23 Anton Kolesov <anton.kolesov@synopsys.com> 2020-04-23 Anton Kolesov <anton.kolesov@synopsys.com>
* elf-bfd.h (elfcore_write_arc_v2): Add prototype. * elf-bfd.h (elfcore_write_arc_v2): Add prototype.

View File

@ -9993,14 +9993,16 @@ get_num_dynamic_syms (Filedata * filedata)
filedata->nbuckets = byte_get (nb, hash_ent_size); filedata->nbuckets = byte_get (nb, hash_ent_size);
filedata->nchains = byte_get (nc, hash_ent_size); filedata->nchains = byte_get (nc, hash_ent_size);
filedata->buckets = get_dynamic_data (filedata, filedata->nbuckets, if (filedata->nbuckets != 0 && filedata->nchains != 0)
hash_ent_size); {
filedata->chains = get_dynamic_data (filedata, filedata->nchains, filedata->buckets = get_dynamic_data (filedata, filedata->nbuckets,
hash_ent_size); hash_ent_size);
filedata->chains = get_dynamic_data (filedata, filedata->nchains,
if (filedata->buckets != NULL && filedata->chains != NULL) hash_ent_size);
num_of_syms = filedata->nchains;
if (filedata->buckets != NULL && filedata->chains != NULL)
num_of_syms = filedata->nchains;
}
no_hash: no_hash:
if (num_of_syms == 0) if (num_of_syms == 0)
{ {
@ -10243,6 +10245,8 @@ process_dynamic_section (Filedata * filedata)
/* Find the appropriate symbol table. */ /* Find the appropriate symbol table. */
if (filedata->dynamic_symbols == NULL || do_histogram) if (filedata->dynamic_symbols == NULL || do_histogram)
{ {
unsigned long num_of_syms;
for (entry = filedata->dynamic_section; for (entry = filedata->dynamic_section;
entry < filedata->dynamic_section + filedata->dynamic_nent; entry < filedata->dynamic_section + filedata->dynamic_nent;
++entry) ++entry)
@ -10262,64 +10266,65 @@ process_dynamic_section (Filedata * filedata)
filedata->dynamic_info_DT_GNU_HASH = entry->d_un.d_val; filedata->dynamic_info_DT_GNU_HASH = entry->d_un.d_val;
} }
if (filedata->dynamic_info[DT_SYMTAB] num_of_syms = get_num_dynamic_syms (filedata);
if (num_of_syms != 0
&& filedata->dynamic_symbols == NULL
&& filedata->dynamic_info[DT_SYMTAB]
&& filedata->dynamic_info[DT_SYMENT]) && filedata->dynamic_info[DT_SYMENT])
{ {
Elf_Internal_Phdr *seg; Elf_Internal_Phdr *seg;
bfd_vma vma = filedata->dynamic_info[DT_SYMTAB]; bfd_vma vma = filedata->dynamic_info[DT_SYMTAB];
if (! get_program_headers (filedata)) if (! get_program_headers (filedata))
{ {
error (_("Cannot interpret virtual addresses without program headers.\n")); error (_("Cannot interpret virtual addresses "
return FALSE; "without program headers.\n"));
} return FALSE;
}
for (seg = filedata->program_headers; for (seg = filedata->program_headers;
seg < filedata->program_headers + filedata->file_header.e_phnum; seg < filedata->program_headers + filedata->file_header.e_phnum;
++seg) ++seg)
{ {
unsigned long num_of_syms; if (seg->p_type != PT_LOAD)
continue;
if (seg->p_type != PT_LOAD) if (seg->p_offset + seg->p_filesz > filedata->file_size)
continue; {
/* See PR 21379 for a reproducer. */
error (_("Invalid PT_LOAD entry\n"));
return FALSE;
}
if ((seg->p_offset + seg->p_filesz) if (vma >= (seg->p_vaddr & -seg->p_align)
> filedata->file_size) && vma < seg->p_vaddr + seg->p_filesz)
{ {
/* See PR 21379 for a reproducer. */ /* Since we do not know how big the symbol table is,
error (_("Invalid PT_LOAD entry\n")); we default to reading in up to the end of PT_LOAD
return FALSE; segment and processing that. This is overkill, I
} know, but it should work. */
Elf_Internal_Shdr section;
if (vma >= (seg->p_vaddr & -seg->p_align) section.sh_offset = (vma - seg->p_vaddr
&& vma <= seg->p_vaddr + seg->p_filesz + seg->p_offset);
&& (num_of_syms = get_num_dynamic_syms (filedata)) != 0 section.sh_size = (num_of_syms
&& filedata->dynamic_symbols == NULL) * filedata->dynamic_info[DT_SYMENT]);
{ section.sh_entsize = filedata->dynamic_info[DT_SYMENT];
/* Since we do not know how big the symbol table is, section.sh_name = filedata->string_table_length;
we default to reading in up to the end of PT_LOAD filedata->dynamic_symbols
segment and processing that. This is overkill, I = GET_ELF_SYMBOLS (filedata, &section,
know, but it should work. */ &filedata->num_dynamic_syms);
Elf_Internal_Shdr section; if (filedata->dynamic_symbols == NULL
section.sh_offset = (vma - seg->p_vaddr || filedata->num_dynamic_syms != num_of_syms)
+ seg->p_offset); {
section.sh_size = (num_of_syms error (_("Corrupt DT_SYMTAB dynamic entry\n"));
* filedata->dynamic_info[DT_SYMENT]); return FALSE;
section.sh_entsize = filedata->dynamic_info[DT_SYMENT]; }
section.sh_name = filedata->string_table_length; break;
filedata->dynamic_symbols }
= GET_ELF_SYMBOLS (filedata, &section, }
&filedata->num_dynamic_syms); }
if (filedata->dynamic_symbols == NULL }
|| filedata->num_dynamic_syms != num_of_syms)
{
error (_("Corrupt DT_SYMTAB dynamic entry\n"));
return FALSE;
}
}
}
}
}
/* Similarly find a string table. */ /* Similarly find a string table. */
if (filedata->dynamic_strings == NULL) if (filedata->dynamic_strings == NULL)
@ -10403,14 +10408,17 @@ process_dynamic_section (Filedata * filedata)
filedata->dynamic_syminfo = (Elf_Internal_Syminfo *) malloc (syminsz); filedata->dynamic_syminfo = (Elf_Internal_Syminfo *) malloc (syminsz);
if (filedata->dynamic_syminfo == NULL) if (filedata->dynamic_syminfo == NULL)
{ {
error (_("Out of memory allocating %lu byte for dynamic symbol info\n"), error (_("Out of memory allocating %lu bytes "
"for dynamic symbol info\n"),
(unsigned long) syminsz); (unsigned long) syminsz);
return FALSE; return FALSE;
} }
filedata->dynamic_syminfo_nent = syminsz / sizeof (Elf_External_Syminfo); filedata->dynamic_syminfo_nent
= syminsz / sizeof (Elf_External_Syminfo);
for (syminfo = filedata->dynamic_syminfo, extsym = extsyminfo; for (syminfo = filedata->dynamic_syminfo, extsym = extsyminfo;
syminfo < filedata->dynamic_syminfo + filedata->dynamic_syminfo_nent; syminfo < (filedata->dynamic_syminfo
+ filedata->dynamic_syminfo_nent);
++syminfo, ++extsym) ++syminfo, ++extsym)
{ {
syminfo->si_boundto = BYTE_GET (extsym->si_boundto); syminfo->si_boundto = BYTE_GET (extsym->si_boundto);
@ -20120,7 +20128,7 @@ process_object (Filedata * filedata)
{ {
bfd_boolean have_separate_files; bfd_boolean have_separate_files;
unsigned int i; unsigned int i;
bfd_boolean res = TRUE; bfd_boolean res;
if (! get_file_header (filedata)) if (! get_file_header (filedata))
{ {
@ -20176,10 +20184,9 @@ process_object (Filedata * filedata)
/* Without loaded section groups we cannot process unwind. */ /* Without loaded section groups we cannot process unwind. */
do_unwind = FALSE; do_unwind = FALSE;
if (process_program_headers (filedata)) res = process_program_headers (filedata);
process_dynamic_section (filedata); if (res)
else res = process_dynamic_section (filedata);
res = FALSE;
if (! process_relocs (filedata)) if (! process_relocs (filedata))
res = FALSE; res = FALSE;