]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
oss-fuzz addr2line mips32_64bit_reloc out-of-bounds accesses
authorAlan Modra <amodra@gmail.com>
Thu, 12 Mar 2026 07:53:28 +0000 (18:23 +1030)
committerAlan Modra <amodra@gmail.com>
Thu, 12 Mar 2026 07:53:28 +0000 (18:23 +1030)
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.

bfd/elf32-mips.c

index 0712cbb0962b522c95123017ad7e918b013abf53..cef0c937d26cc53b5a9904d42c5bffdb484acd68 100644 (file)
@@ -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;
 }