Re: readelf: Save and dump the original section header values

This is a modified version of the mainline patch without fixes for
warning messages, so that translations do not need to be updated.

I decided it wasn't worth reporting specifics about
a malloc failure when allocating a relatively small fixed size buffer.
It would be different if some large chunk of file data caused a malloc
fail.

The patch also fixes a potential orig_section_headers buffer overflow,
caused by allocating a single element array when probing, hitting some
early error in process_file_header, then attempting to access it as an
array of e_shnum entries in free_filedata.

	* readelf.c (save_original_section_header_values): Use xmalloc
	rather than reporting a more specific error.  Drop now unused
	function parameters.
	(validate_section_info): Delete "dynamic" and "probe" params.
	Don't save orig header for fake section header passing relr
	dynamic tags.  Avoid possible arithmetic overflow when
	checking sh_size.
	(get_32bit_section_headers): Don't allocate orig_section_headers
	or call validate_section_info when probing.
	(get_64bit_section_headers): Likewise.
	(process_relocs): Update validate_section_info call when
	handling relr.

(cherry picked from commit e7c25d560b)
This commit is contained in:
Alan Modra 2026-07-12 18:25:35 +09:30
parent cdba41388c
commit 29f50f5760

View file

@ -7853,58 +7853,26 @@ offset_from_vma (Filedata * filedata, uint64_t vma, uint64_t size)
static void
save_original_section_header_values (Elf_Internal_Shdr *internal,
Elf_Internal_Shdr **orig_internal,
const char *dynamic_tag,
unsigned int i)
Elf_Internal_Shdr **orig_internal)
{
/* Return if the original section header values have been saved. */
/* Nothing to do if they have already been saved. */
if (*orig_internal != NULL)
return;
*orig_internal = (Elf_Internal_Shdr *)
malloc (sizeof (Elf_Internal_Shdr));
if (*orig_internal == NULL)
{
if (dynamic_tag)
error (_("Out of memory reading dynamic tag %s\n"),
dynamic_tag);
else
error (_("Out of memory reading %u section headers\n"), i);
return;
}
/* Save the original section header values. */
*orig_internal = xmalloc (sizeof (**orig_internal));
**orig_internal = *internal;
}
/* Valid section info and clear the invalid fields. */
/* Warn about and clear any invalid ELF section fields. */
static void
validate_section_info (Elf_Internal_Shdr *internal,
Elf_Internal_Shdr **orig_internal,
unsigned int i, Filedata *filedata, bool dynamic,
bool probe)
unsigned int i, Filedata *filedata)
{
const char *dynamic_tag = NULL;
const char *dynamicsz_tag = NULL;
const char *dynamicent_tag = NULL;
if (probe)
return;
if (dynamic)
switch (i)
{
case DT_RELR:
dynamic_tag = "DT_RELR";
dynamicsz_tag = "DT_RELRSZ";
dynamicent_tag = "DT_RELRENT";
break;
default:
abort ();
}
else
bool dynamic = orig_internal == NULL;
if (!dynamic)
{
if (internal->sh_link >= filedata->file_header.e_shnum
&& !special_defined_section_index (filedata,
@ -7914,8 +7882,7 @@ validate_section_info (Elf_Internal_Shdr *internal,
"section %u\n"), internal->sh_link, i);
/* Save the original section header values before garbage
values are cleared. */
save_original_section_header_values (internal, orig_internal,
NULL, i);
save_original_section_header_values (internal, orig_internal);
internal->sh_link = 0;
}
@ -7924,8 +7891,7 @@ validate_section_info (Elf_Internal_Shdr *internal,
{
warn (_("Ignore the out of range sh_info value of %u for "
"section %u\n"), internal->sh_info, i);
save_original_section_header_values (internal, orig_internal,
NULL, i);
save_original_section_header_values (internal, orig_internal);
internal->sh_info = 0;
}
}
@ -7935,45 +7901,48 @@ validate_section_info (Elf_Internal_Shdr *internal,
if (dynamic)
warn (_("Ignore the out of range value of %" PRIu64 " for "
"dynamic tag %s\n"), (uint64_t) internal->sh_entsize,
dynamicent_tag);
"DT_RELRENT");
else
warn (_("Ignore the out of range sh_entsize value of %"
PRIu64 " for section %u\n"),
{
warn (_("Ignore the out of range sh_entsize value of %"
PRIu64 " for section %u\n"),
(uint64_t) internal->sh_entsize, i);
save_original_section_header_values (internal, orig_internal,
dynamicent_tag, i);
save_original_section_header_values (internal, orig_internal);
}
internal->sh_entsize = 0;
}
if (internal->sh_type != SHT_NOBITS)
{
int64_t sh_offset = internal->sh_offset;
if (sh_offset < 0 || (uint64_t) sh_offset > filedata->file_size)
uint64_t sh_offset = internal->sh_offset;
if (sh_offset > filedata->file_size)
{
if (dynamic)
warn (_("Ignore the out of range value of %" PRId64 " for "
"dynamic tag %s\n"), sh_offset, dynamic_tag);
"dynamic tag %s\n"), sh_offset, "DT_RELR");
else
warn (_("Ignore the out of range sh_offset value of %"
PRId64 " for section %u\n"), sh_offset, i);
save_original_section_header_values (internal, orig_internal,
dynamic_tag, i);
{
warn (_("Ignore the out of range sh_offset value of %"
PRId64 " for section %u\n"), sh_offset, i);
save_original_section_header_values (internal, orig_internal);
}
internal->sh_offset = 0;
internal->sh_size = 0;
}
if (sh_offset + internal->sh_size > filedata->file_size)
else if (internal->sh_size > filedata->file_size - sh_offset)
{
if (dynamic)
warn (_("Ignore the out of range value of %" PRIu64 " for "
"dynamic tag %s\n"), (uint64_t) internal->sh_size,
dynamicsz_tag);
"DT_RELRSZ");
else
warn (_("Ignore the out of range sh_size value of %"
PRIu64 " for section %u with sh_offset value of %"
PRId64 "\n"), (uint64_t) internal->sh_size, i,
sh_offset);
save_original_section_header_values (internal, orig_internal,
dynamicsz_tag, i);
{
warn (_("Ignore the out of range sh_size value of %"
PRIu64 " for section %u with sh_offset value of %"
PRId64 "\n"), (uint64_t) internal->sh_size, i,
sh_offset);
save_original_section_header_values (internal, orig_internal);
}
internal->sh_size = 0;
}
}
@ -8028,8 +7997,9 @@ get_32bit_section_headers (Filedata * filedata, bool probe)
return false;
}
filedata->orig_section_headers = (Elf_Internal_Shdr **)
xcalloc2 (num, sizeof (Elf_Internal_Shdr *));
if (!probe)
filedata->orig_section_headers = xcalloc2 (num,
sizeof (Elf_Internal_Shdr *));
orig_internal = filedata->orig_section_headers;
for (i = 0, internal = filedata->section_headers;
@ -8046,8 +8016,8 @@ get_32bit_section_headers (Filedata * filedata, bool probe)
internal->sh_info = BYTE_GET (shdrs[i].sh_info);
internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
internal->sh_entsize = BYTE_GET (shdrs[i].sh_entsize);
validate_section_info (internal, orig_internal, i, filedata,
false, probe);
if (!probe)
validate_section_info (internal, orig_internal, i, filedata);
}
free (shdrs);
@ -8103,8 +8073,9 @@ get_64bit_section_headers (Filedata * filedata, bool probe)
return false;
}
filedata->orig_section_headers = (Elf_Internal_Shdr **)
xcalloc2 (num, sizeof (Elf_Internal_Shdr *));
if (!probe)
filedata->orig_section_headers = xcalloc2 (num,
sizeof (Elf_Internal_Shdr *));
orig_internal = filedata->orig_section_headers;
for (i = 0, internal = filedata->section_headers;
@ -8121,8 +8092,8 @@ get_64bit_section_headers (Filedata * filedata, bool probe)
internal->sh_info = BYTE_GET (shdrs[i].sh_info);
internal->sh_offset = BYTE_GET (shdrs[i].sh_offset);
internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
validate_section_info (internal, orig_internal, i, filedata,
false, probe);
if (!probe)
validate_section_info (internal, orig_internal, i, filedata);
}
free (shdrs);
@ -10261,18 +10232,15 @@ process_relocs (Filedata * filedata)
uint64_t num_reloc;
uint64_t *relrs = NULL;
Elf_Internal_Shdr section = {};
Elf_Internal_Shdr *orig_section = NULL;
section.sh_offset
= filedata->dynamic_info[DT_RELR];
section.sh_size = rel_size;
section.sh_entsize = rel_entsz;
section.sh_type = SHT_RELR;
validate_section_info (&section, &orig_section, DT_RELR,
filedata, true, false);
validate_section_info (&section, NULL, 0, filedata);
num_reloc = count_relr_relocations (filedata,
&section,
&relrs);
free (orig_section);
free (relrs);
if (num_reloc == 0)
continue;