oss-fuzz mips32_64bit_reloc out-of-bounds accesses

Alan Modra amodra@gmail.com
Wed Mar 4 23:25:29 GMT 2026


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.  However, taking notice of that result
isn't sufficient since in little-endian mode a testcase could be
crafted that 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 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.
Also, don't trim addends to 32 bits for relocable linking or gas.
That doesn't seem necessary and in any case wasn't done for the rela
R_MIPS_64.

	* elf32-mips.c (mips32_64bit_reloc): Rewrite.

OK?

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;
 }
 

-- 
Alan Modra


More information about the Binutils mailing list