From f6591b4749def1cc37fefe49e32cedd95e2fdbaa Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 11 Feb 2025 12:50:33 +0000 Subject: [PATCH] Stop a call to abort in the linker when processing fuzzed input. (#2344837) Fix an illegal memory access when the linker processed a fuzzed input file. (#2344835) --- binutils-ld-gen-dynamic-relocs.patch | 28 ++ binutils-ld-sym-hashes.patch | 464 +++++++++++++++++++++++++++ binutils.spec | 16 +- 3 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 binutils-ld-gen-dynamic-relocs.patch create mode 100644 binutils-ld-sym-hashes.patch diff --git a/binutils-ld-gen-dynamic-relocs.patch b/binutils-ld-gen-dynamic-relocs.patch new file mode 100644 index 0000000..665397a --- /dev/null +++ b/binutils-ld-gen-dynamic-relocs.patch @@ -0,0 +1,28 @@ +From 75086e9de1707281172cc77f178e7949a4414ed0 Mon Sep 17 00:00:00 2001 +From: Nick Clifton +Date: Wed, 5 Feb 2025 13:26:51 +0000 +Subject: [PATCH] Prevent an abort in the bfd linker when attempting to + generate dynamic relocs for a corrupt input file. + +PR 32638 + +diff -rup binutils.orig/bfd/elf64-x86-64.c binutils-2.43.1/bfd/elf64-x86-64.c +--- binutils.orig/bfd/elf64-x86-64.c 2025-02-11 10:40:56.262887171 +0000 ++++ binutils-2.43.1/bfd/elf64-x86-64.c 2025-02-11 10:41:26.312036385 +0000 +@@ -4951,6 +4951,15 @@ elf_x86_64_finish_dynamic_symbol (bfd *o + + if (generate_dynamic_reloc) + { ++ /* If the relgot section has not been created, then ++ generate an error instead of a reloc. cf PR 32638. */ ++ if (relgot == NULL || relgot->size == 0) ++ { ++ info->callbacks->einfo (_("%F%pB: Unable to generate dynamic relocs because a suitable section does not exist\n"), ++ output_bfd); ++ return false; ++ } ++ + if (relative_reloc_name != NULL + && htab->params->report_relative_reloc) + _bfd_x86_elf_link_report_relative_reloc +Only in binutils-2.43.1/bfd: elf64-x86-64.c.orig diff --git a/binutils-ld-sym-hashes.patch b/binutils-ld-sym-hashes.patch new file mode 100644 index 0000000..20b85d5 --- /dev/null +++ b/binutils-ld-sym-hashes.patch @@ -0,0 +1,464 @@ +commit b425859021d17adf62f06fb904797cf8642986ad +Author: Nick Clifton +Date: Wed Feb 5 16:27:38 2025 +0000 + + Fix another illegal memory access triggered by corrupt ELF input files. + + PR 32644 + +commit 931494c9a89558acb36a03a340c01726545eef24 +Author: Nick Clifton +Date: Wed Feb 5 15:43:04 2025 +0000 + + Add even more checks for corrupt input when processing relocations for ELF files. + + PR 32643 + +commit 18cc11a2771d9e40180485da9a4fb660c03efac3 +Author: Nick Clifton +Date: Wed Feb 5 14:31:10 2025 +0000 + + Prevent illegal memory access when checking relocs in a corrupt ELF binary. + + PR 32641 + +commit f9978defb6fab0bd8583942d97c112b0932ac814 +Author: Nick Clifton +Date: Wed Feb 5 11:15:11 2025 +0000 + + Prevent illegal memory access when indexing into the sym_hashes array of the elf bfd cookie structure. + + PR 32636 + +diff -rup binutils.orig/bfd/elf-bfd.h binutils-2.43.1/bfd/elf-bfd.h +--- binutils.orig/bfd/elf-bfd.h 2025-02-11 10:58:25.223874250 +0000 ++++ binutils-2.43.1/bfd/elf-bfd.h 2025-02-11 11:13:12.177965286 +0000 +@@ -3147,6 +3147,9 @@ extern bool _bfd_elf_link_mmap_section_c + extern void _bfd_elf_link_munmap_section_contents + (asection *); + ++extern struct elf_link_hash_entry * _bfd_elf_get_link_hash_entry ++ (struct elf_link_hash_entry **, unsigned int, Elf_Internal_Shdr *); ++ + /* Large common section. */ + extern asection _bfd_elf_large_com_section; + +diff -rup binutils.orig/bfd/elflink.c binutils-2.43.1/bfd/elflink.c +--- binutils.orig/bfd/elflink.c 2025-02-11 10:58:25.256874290 +0000 ++++ binutils-2.43.1/bfd/elflink.c 2025-02-11 11:11:22.536375589 +0000 +@@ -96,22 +96,64 @@ _bfd_elf_link_keep_memory (struct bfd_li + return true; + } + +-asection * +-_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie, +- unsigned long r_symndx, +- bool discard) ++static struct elf_link_hash_entry * ++get_link_hash_entry (struct elf_link_hash_entry ** sym_hashes, ++ unsigned int symndx, ++ unsigned int ext_sym_start) ++{ ++ if (sym_hashes == NULL ++ /* Guard against corrupt input. See PR 32636 for an example. */ ++ || symndx < ext_sym_start) ++ return NULL; ++ ++ struct elf_link_hash_entry *h = sym_hashes[symndx - ext_sym_start]; ++ ++ /* The hash might be empty. See PR 32641 for an example of this. */ ++ if (h == NULL) ++ return NULL; ++ ++ while (h->root.type == bfd_link_hash_indirect ++ || h->root.type == bfd_link_hash_warning) ++ h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ ++ return h; ++} ++ ++struct elf_link_hash_entry * ++_bfd_elf_get_link_hash_entry (struct elf_link_hash_entry ** sym_hashes, ++ unsigned int symndx, ++ Elf_Internal_Shdr * symtab_hdr) ++{ ++ if (symtab_hdr == NULL) ++ return NULL; ++ ++ return get_link_hash_entry (sym_hashes, symndx, symtab_hdr->sh_info); ++} ++ ++static struct elf_link_hash_entry * ++get_ext_sym_hash_from_cookie (struct elf_reloc_cookie *cookie, unsigned long r_symndx) + { ++ if (cookie == NULL || cookie->sym_hashes == NULL) ++ return NULL; ++ + if (r_symndx >= cookie->locsymcount + || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL) +- { +- struct elf_link_hash_entry *h; ++ return get_link_hash_entry (cookie->sym_hashes, r_symndx, cookie->extsymoff); + +- h = cookie->sym_hashes[r_symndx - cookie->extsymoff]; ++ return NULL; ++} + +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++asection * ++_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie, ++ unsigned long r_symndx, ++ bool discard) ++{ ++ struct elf_link_hash_entry *h; + ++ h = get_ext_sym_hash_from_cookie (cookie, r_symndx); ++ ++ if (h != NULL) ++ { + if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && discarded_section (h->root.u.def.section)) +@@ -119,21 +161,20 @@ _bfd_elf_section_for_symbol (struct elf_ + else + return NULL; + } +- else +- { +- /* It's not a relocation against a global symbol, +- but it could be a relocation against a local +- symbol for a discarded section. */ +- asection *isec; +- Elf_Internal_Sym *isym; + +- /* Need to: get the symbol; get the section. */ +- isym = &cookie->locsyms[r_symndx]; +- isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx); +- if (isec != NULL +- && discard ? discarded_section (isec) : 1) +- return isec; +- } ++ /* It's not a relocation against a global symbol, ++ but it could be a relocation against a local ++ symbol for a discarded section. */ ++ asection *isec; ++ Elf_Internal_Sym *isym; ++ ++ /* Need to: get the symbol; get the section. */ ++ isym = &cookie->locsyms[r_symndx]; ++ isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx); ++ if (isec != NULL ++ && discard ? discarded_section (isec) : 1) ++ return isec; ++ + return NULL; + } + +@@ -9058,7 +9099,6 @@ set_symbol_value (bfd *bfd_with_globals, + size_t symidx, + bfd_vma val) + { +- struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry *h; + size_t extsymoff = locsymcount; + +@@ -9081,12 +9121,12 @@ set_symbol_value (bfd *bfd_with_globals, + + /* It is a global symbol: set its link type + to "defined" and give it a value. */ +- +- sym_hashes = elf_sym_hashes (bfd_with_globals); +- h = sym_hashes [symidx - extsymoff]; +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ h = get_link_hash_entry (elf_sym_hashes (bfd_with_globals), symidx, extsymoff); ++ if (h == NULL) ++ { ++ /* FIXMEL What should we do ? */ ++ return; ++ } + h->root.type = bfd_link_hash_defined; + h->root.u.def.value = val; + h->root.u.def.section = bfd_abs_section_ptr; +@@ -11568,10 +11608,19 @@ elf_link_input_bfd (struct elf_final_lin + || (elf_bad_symtab (input_bfd) + && flinfo->sections[symndx] == NULL)) + { +- struct elf_link_hash_entry *h = sym_hashes[symndx - extsymoff]; +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ struct elf_link_hash_entry *h; ++ ++ h = get_link_hash_entry (sym_hashes, symndx, extsymoff); ++ if (h == NULL) ++ { ++ _bfd_error_handler ++ /* xgettext:c-format */ ++ (_("error: %pB: unable to create group section symbol"), ++ input_bfd); ++ bfd_set_error (bfd_error_bad_value); ++ return false; ++ } ++ + /* Arrange for symbol to be output. */ + h->indx = -2; + elf_section_data (osec)->this_hdr.sh_info = -2; +@@ -11706,7 +11755,7 @@ elf_link_input_bfd (struct elf_final_lin + || (elf_bad_symtab (input_bfd) + && flinfo->sections[r_symndx] == NULL)) + { +- h = sym_hashes[r_symndx - extsymoff]; ++ h = get_link_hash_entry (sym_hashes, r_symndx, extsymoff); + + /* Badly formatted input files can contain relocs that + reference non-existant symbols. Check here so that +@@ -11715,17 +11764,13 @@ elf_link_input_bfd (struct elf_final_lin + { + _bfd_error_handler + /* xgettext:c-format */ +- (_("error: %pB contains a reloc (%#" PRIx64 ") for section %pA " ++ (_("error: %pB contains a reloc (%#" PRIx64 ") for section '%pA' " + "that references a non-existent global symbol"), + input_bfd, (uint64_t) rel->r_info, o); + bfd_set_error (bfd_error_bad_value); + return false; + } + +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; +- + s_type = h->type; + + /* If a plugin symbol is referenced from a non-IR file, +@@ -11941,7 +11986,6 @@ elf_link_input_bfd (struct elf_final_lin + && flinfo->sections[r_symndx] == NULL)) + { + struct elf_link_hash_entry *rh; +- unsigned long indx; + + /* This is a reloc against a global symbol. We + have not yet output all the local symbols, so +@@ -11950,11 +11994,13 @@ elf_link_input_bfd (struct elf_final_lin + reloc to point to the global hash table entry + for this symbol. The symbol index is then + set at the end of bfd_elf_final_link. */ +- indx = r_symndx - extsymoff; +- rh = elf_sym_hashes (input_bfd)[indx]; +- while (rh->root.type == bfd_link_hash_indirect +- || rh->root.type == bfd_link_hash_warning) +- rh = (struct elf_link_hash_entry *) rh->root.u.i.link; ++ rh = get_link_hash_entry (elf_sym_hashes (input_bfd), ++ r_symndx, extsymoff); ++ if (rh == NULL) ++ { ++ /* FIXME: Generate an error ? */ ++ continue; ++ } + + /* Setting the index to -2 tells + elf_link_output_extsym that this symbol is +@@ -13958,25 +14004,21 @@ _bfd_elf_gc_mark_hook (asection *sec, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) + { +- if (h != NULL) ++ if (h == NULL) ++ return bfd_section_from_elf_index (sec->owner, sym->st_shndx); ++ ++ switch (h->root.type) + { +- switch (h->root.type) +- { +- case bfd_link_hash_defined: +- case bfd_link_hash_defweak: +- return h->root.u.def.section; ++ case bfd_link_hash_defined: ++ case bfd_link_hash_defweak: ++ return h->root.u.def.section; + +- case bfd_link_hash_common: +- return h->root.u.c.p->section; ++ case bfd_link_hash_common: ++ return h->root.u.c.p->section; + +- default: +- break; +- } ++ default: ++ return NULL; + } +- else +- return bfd_section_from_elf_index (sec->owner, sym->st_shndx); +- +- return NULL; + } + + /* Return the debug definition section. */ +@@ -14025,56 +14067,49 @@ _bfd_elf_gc_mark_rsec (struct bfd_link_i + if (r_symndx == STN_UNDEF) + return NULL; + +- if (r_symndx >= cookie->locsymcount +- || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL) ++ h = get_ext_sym_hash_from_cookie (cookie, r_symndx); ++ if (h == NULL) + { +- bool was_marked; ++ /* A corrup tinput file can lead to a situation where the index ++ does not reference either a local or an external symbol. */ ++ if (r_symndx >= cookie->locsymcount) ++ return NULL; + +- h = cookie->sym_hashes[r_symndx - cookie->extsymoff]; +- if (h == NULL) +- { +- info->callbacks->einfo (_("%F%P: corrupt input: %pB\n"), +- sec->owner); +- return NULL; +- } +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ return (*gc_mark_hook) (sec, info, cookie->rel, NULL, ++ &cookie->locsyms[r_symndx]); ++ } + +- was_marked = h->mark; +- h->mark = 1; +- /* Keep all aliases of the symbol too. If an object symbol +- needs to be copied into .dynbss then all of its aliases +- should be present as dynamic symbols, not just the one used +- on the copy relocation. */ +- hw = h; +- while (hw->is_weakalias) +- { +- hw = hw->u.alias; +- hw->mark = 1; +- } ++ bool was_marked = h->mark; + +- if (!was_marked && h->start_stop && !h->root.ldscript_def) +- { +- if (info->start_stop_gc) +- return NULL; ++ h->mark = 1; ++ /* Keep all aliases of the symbol too. If an object symbol ++ needs to be copied into .dynbss then all of its aliases ++ should be present as dynamic symbols, not just the one used ++ on the copy relocation. */ ++ hw = h; ++ while (hw->is_weakalias) ++ { ++ hw = hw->u.alias; ++ hw->mark = 1; ++ } + +- /* To work around a glibc bug, mark XXX input sections +- when there is a reference to __start_XXX or __stop_XXX +- symbols. */ +- else if (start_stop != NULL) +- { +- asection *s = h->u2.start_stop_section; +- *start_stop = true; +- return s; +- } +- } ++ if (!was_marked && h->start_stop && !h->root.ldscript_def) ++ { ++ if (info->start_stop_gc) ++ return NULL; + +- return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL); ++ /* To work around a glibc bug, mark XXX input sections ++ when there is a reference to __start_XXX or __stop_XXX ++ symbols. */ ++ else if (start_stop != NULL) ++ { ++ asection *s = h->u2.start_stop_section; ++ *start_stop = true; ++ return s; ++ } + } + +- return (*gc_mark_hook) (sec, info, cookie->rel, NULL, +- &cookie->locsyms[r_symndx]); ++ return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL); + } + + /* COOKIE->rel describes a relocation against section SEC, which is +@@ -15095,17 +15130,12 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma + if (r_symndx == STN_UNDEF) + return true; + +- if (r_symndx >= rcookie->locsymcount +- || ELF_ST_BIND (rcookie->locsyms[r_symndx].st_info) != STB_LOCAL) +- { +- struct elf_link_hash_entry *h; +- +- h = rcookie->sym_hashes[r_symndx - rcookie->extsymoff]; +- +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ struct elf_link_hash_entry *h; + ++ h = get_ext_sym_hash_from_cookie (rcookie, r_symndx); ++ ++ if (h != NULL) ++ { + if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section->owner != rcookie->abfd +@@ -15115,6 +15145,10 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma + } + else + { ++ if (r_symndx >= rcookie->locsymcount) ++ /* This can happen with corrupt input. */ ++ return false; ++ + /* It's not a relocation against a global symbol, + but it could be a relocation against a local + symbol for a discarded section. */ +diff -rup binutils.orig/bfd/elfxx-x86.c binutils-2.43.1/bfd/elfxx-x86.c +--- binutils.orig/bfd/elfxx-x86.c 2025-02-11 10:58:25.307874350 +0000 ++++ binutils-2.43.1/bfd/elfxx-x86.c 2025-02-11 11:15:21.937392450 +0000 +@@ -972,15 +972,7 @@ _bfd_x86_elf_check_relocs (bfd *abfd, + goto error_return; + } + +- if (r_symndx < symtab_hdr->sh_info) +- h = NULL; +- else +- { +- h = sym_hashes[r_symndx - symtab_hdr->sh_info]; +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; +- } ++ h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx, symtab_hdr); + + if (X86_NEED_DYNAMIC_RELOC_TYPE_P (is_x86_64, r_type) + && NEED_DYNAMIC_RELOCATION_P (is_x86_64, info, true, h, sec, +@@ -1205,10 +1197,12 @@ _bfd_x86_elf_link_relax_section (bfd *ab + else + { + /* Get H and SEC for GENERATE_DYNAMIC_RELOCATION_P below. */ +- h = sym_hashes[r_symndx - symtab_hdr->sh_info]; +- while (h->root.type == bfd_link_hash_indirect +- || h->root.type == bfd_link_hash_warning) +- h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ h = _bfd_elf_get_link_hash_entry (sym_hashes, r_symndx, symtab_hdr); ++ if (h == NULL) ++ { ++ /* FIXMEL: Issue an error message ? */ ++ continue; ++ } + + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) diff --git a/binutils.spec b/binutils.spec index 4112092..d811bd2 100644 --- a/binutils.spec +++ b/binutils.spec @@ -7,7 +7,7 @@ Name: binutils%{?_with_debug:-debug} # The variable %%{source} (see below) should be set to indicate which of these # origins is being used. Version: 2.43.1 -Release: 5%{?dist} +Release: 6%{?dist} License: GPL-3.0-or-later AND (GPL-3.0-or-later WITH Bison-exception-2.2) AND (LGPL-2.0-or-later WITH GCC-exception-2.0) AND BSD-3-Clause AND GFDL-1.3-or-later AND GPL-2.0-or-later AND LGPL-2.1-or-later AND LGPL-2.0-or-later URL: https://sourceware.org/binutils @@ -316,6 +316,16 @@ Patch21: binutils-ppc64-TLS-no_plt-optimization-bug.patch # Lifetime: Fixed in 2.44 Patch22: binutils-linker-script-build-id-placement.patch +# Purpose: Stops a call to abort in the linker when processing fuzzed input. +# Lifetime: Fixed in 2.45 +Patch23: binutils-ld-gen-dynamic-relocs.patch + +# Purpose: Stops a call to abort in the linker when processing fuzzed input. +# Lifetime: Fixed in 2.45 +Patch24: binutils-ld-sym-hashes.patch + +#---------------------------------------------------------------------------- + # Purpose: Suppress the x86 linker's p_align-1 tests due to kernel bug on CentOS-10 # Lifetime: TEMPORARY Patch99: binutils-suppress-ld-align-tests.patch @@ -1374,6 +1384,10 @@ exit 0 #---------------------------------------------------------------------------- %changelog +* Tue Feb 11 2025 Nick Clifton - 2.43.1-6 +- Stop a call to abort in the linker when processing fuzzed input. (#2344837) +- Fix an illegal memory access when the linker processed a fuzzed input file. (#2344835) + * Fri Dec 13 2024 Björn Esser - 2.43.1-5 - Restore the .note.build-id section placement patch. (#2331487)