[binutils-gdb/binutils-2_47-branch] Re: readelf: Save and dump the original section header values
Alan Modra via Binutils-cvs <[email protected]> Thu, 16 Jul 2026 00:18:28 +0000 (GMT)
| Newsgroups | gmane.comp.gnu.binutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=29f50f5760e97d77d4f0fac572f1237bfd647398 commit 29f50f5760e97d77d4f0fac572f1237bfd647398 Author: Alan Modra <[email protected]> Date: Sun Jul 12 18:25:35 2026 +0930 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 e7c25d560b0ee365a80dc953285f8e84f622f0b5) Diff: --- binutils/readelf.c | 120 ++++++++++++++++++++--------------------------------- 1 file changed, 44 insertions(+), 76 deletions(-) diff --git a/binutils/readelf.c b/binutils/readelf.c index 0938fe64b22..8b6ebf526df 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -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 (§ion, &orig_section, DT_RELR, - filedata, true, false); + validate_section_info (§ion, NULL, 0, filedata); num_reloc = count_relr_relocations (filedata, §ion, &relrs); - free (orig_section); free (relrs); if (num_reloc == 0) continue;