[binutils-gdb] oss-fuzz addr2line mips32_64bit_reloc out-of-bounds accesses
Alan Modra via Binutils-cvs <[email protected]>
| Newsgroups | gmane.comp.gnu.binutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=c8b798145ff623a87ec4d39015cf832b0160899d commit c8b798145ff623a87ec4d39015cf832b0160899d Author: Alan Modra <[email protected]> Date: Thu Mar 12 18:23:28 2026 +1030 oss-fuzz addr2line mips32_64bit_reloc out-of-bounds accesses The code sign extending the low 32 bits into the high 32 bits in this function does not have any address checking as it ignores the result from bfd_perform_relocation. Even if it did take notice that isn't sufficient. In little-endian mode a testcase could be crafted with the low word at the end of a section which would put the high word out of bounds. I notice also that mips32_64bit_reloc is called from a rela R_MIPS_64 reloc howto. Using a rel R_MIPS_32 howto for the lower word can't be correct as it retrieves the addend from section contents rather than the reloc. Fix both of these problems by performing the entire normal 64-bit relocation first rather than trying to be clever doing it by pieces. Nowadays config.bfd ensures that mips elf targets always have a 64-bit bfd. Also, don't trim addends to 32 bits for relocatable linking or gas. That doesn't seem necessary and in any case wasn't done properly for the rela case, where the addend is not in section contents but in the relocation. Note that relocatable linking will usually be done by elfxx-mips.c:_bfd_mips_elf_relocate_section. The main use of these howtos is in gas and bfd_simple_get_relocated_section_contents. * elf32-mips.c (mips32_64bit_reloc): Rewrite. Diff: --- bfd/elf32-mips.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index 0712cbb0962..cef0c937d26 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -3339,35 +3339,22 @@ gprel32_with_gp (bfd *abfd, asymbol *symbol, arelent *reloc_entry, sign extension. */ static bfd_reloc_status_type -mips32_64bit_reloc (bfd *abfd, arelent *reloc_entry, - asymbol *symbol ATTRIBUTE_UNUSED, +mips32_64bit_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, void *data, asection *input_section, bfd *output_bfd, char **error_message) { bfd_reloc_status_type r; - arelent reloc32; - unsigned long val; - bfd_size_type addr; - - /* Do a normal 32 bit relocation on the lower 32 bits. */ - reloc32 = *reloc_entry; - if (bfd_big_endian (abfd)) - reloc32.address += 4; - reloc32.howto = &elf_mips_howto_table_rel[R_MIPS_32]; - r = bfd_perform_relocation (abfd, &reloc32, data, input_section, - output_bfd, error_message); - - /* Sign extend into the upper 32 bits. */ - val = bfd_get_32 (abfd, (bfd_byte *) data + reloc32.address); - if ((val & 0x80000000) != 0) - val = 0xffffffff; - else - val = 0; - addr = reloc_entry->address; - if (bfd_little_endian (abfd)) - addr += 4; - bfd_put_32 (abfd, val, (bfd_byte *) data + addr); + r = _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + if (r == bfd_reloc_ok && output_bfd == NULL) + { + /* When final linking, sign extend low word into the upper word. */ + bfd_byte *loc = (bfd_byte *) data + reloc_entry->address; + bfd_vma val = bfd_get_64 (abfd, loc); + val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000; + bfd_put_64 (abfd, val, loc); + } return r; }