mips-coff: mips_reflo_reloc buffer overflow

Alan Modra amodra@gmail.com
Thu Jun 11 07:08:10 GMT 2026


Like commit c2cb1bfb901e, but for mips-coff.  Make the hi reloc list
per-section rather than per-file, since fuzzers can generate objects
with cross-section refs where the hi reloc section data is freed
before the lo relocs are processed.

Also properly sanity check the hi reloc offset.

	* libecoff.h (struct ecoff_tdata): Move mips_refhi_list to..
	(struct ecoff_section_tdata): ..here.
	(ecoff_section_data): Remove unused abfd arg.
	* coff-alpha.c (alpha_relocate_section): Update ecoff_section_data.
	* coff-mips.c (mips_refhi_reloc): Use bfd_reloc_offset_in_range.
	Allocate ecoff_section_data as necessary.  Use that instead of
	bfd tdata.
	(mips_reflo_reloc): Adjust to suit tdata change.
	* ecoff.c (_bfd_ecoff_bfd_free_cached_info): Likewise.

diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 0396a1fd9fc..35677e9a933 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -1445,7 +1445,7 @@ alpha_relocate_section (bfd *output_bfd,
 
       /* Make sure we have a section data structure to which we can
 	 hang on to the gp value we pick for the section.  */
-      lita_sec_data = ecoff_section_data (input_bfd, lita_sec);
+      lita_sec_data = ecoff_section_data (lita_sec);
       if (lita_sec_data == NULL)
 	{
 	  lita_sec_data = bfd_zalloc (input_bfd, sizeof (*lita_sec_data));
diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c
index 85abe9f4360..35848d94ca1 100644
--- a/bfd/coff-mips.c
+++ b/bfd/coff-mips.c
@@ -460,17 +460,29 @@ mips_refhi_reloc (bfd *abfd,
   relocation += symbol->section->output_offset;
   relocation += reloc_entry->addend;
 
-  if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+  bfd_size_type octet = (reloc_entry->address
+			 * OCTETS_PER_BYTE (abfd, input_section));
+  if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
+				  input_section, octet))
     return bfd_reloc_outofrange;
 
+  struct ecoff_section_tdata *sdata = ecoff_section_data (input_section);
+  if (sdata == NULL)
+    {
+      sdata = bfd_zalloc (abfd, sizeof (*sdata));
+      if (sdata == NULL)
+	return bfd_reloc_outofrange;
+      input_section->used_by_bfd = sdata;
+    }
+
   /* Save the information, and let REFLO do the actual relocation.  */
-  n = (struct mips_hi *) bfd_malloc ((bfd_size_type) sizeof *n);
+  n = bfd_malloc (sizeof (*n));
   if (n == NULL)
     return bfd_reloc_outofrange;
   n->addr = (bfd_byte *) data + reloc_entry->address;
   n->addend = relocation;
-  n->next = ecoff_data (abfd)->mips_refhi_list;
-  ecoff_data (abfd)->mips_refhi_list = n;
+  n->next = sdata->mips_refhi_list;
+  sdata->mips_refhi_list = n;
 
   if (output_bfd != (bfd *) NULL)
     reloc_entry->address += input_section->output_offset;
@@ -491,11 +503,11 @@ mips_reflo_reloc (bfd *abfd,
 		  bfd *output_bfd,
 		  char **error_message)
 {
-  if (ecoff_data (abfd)->mips_refhi_list != NULL)
+  struct ecoff_section_tdata *sdata = ecoff_section_data (input_section);
+  if (sdata != NULL)
     {
-      struct mips_hi *l;
+      struct mips_hi *l = sdata->mips_refhi_list;
 
-      l = ecoff_data (abfd)->mips_refhi_list;
       while (l != NULL)
 	{
 	  unsigned long insn;
@@ -537,8 +549,7 @@ mips_reflo_reloc (bfd *abfd,
 	  free (l);
 	  l = next;
 	}
-
-      ecoff_data (abfd)->mips_refhi_list = NULL;
+      sdata->mips_refhi_list = NULL;
     }
 
   /* Now do the REFLO reloc in the usual way.  */
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index cd56a46c460..906fe44725e 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -117,11 +117,18 @@ _bfd_ecoff_bfd_free_cached_info (bfd *abfd)
   if (bfd_get_format (abfd) == bfd_object
       && (tdata = ecoff_data (abfd)) != NULL)
     {
-      while (tdata->mips_refhi_list != NULL)
+      for (asection *sec = abfd->sections; sec != NULL; sec = sec->next)
 	{
-	  struct mips_hi *ref = tdata->mips_refhi_list;
-	  tdata->mips_refhi_list = ref->next;
-	  free (ref);
+	  struct ecoff_section_tdata *sdata = ecoff_section_data (sec);
+	  if (sdata != NULL)
+	    {
+	      while (sdata->mips_refhi_list != NULL)
+		{
+		  struct mips_hi *ref = sdata->mips_refhi_list;
+		  sdata->mips_refhi_list = ref->next;
+		  free (ref);
+		}
+	    }
 	}
       _bfd_ecoff_free_ecoff_debug_info (&tdata->debug_info);
     }
diff --git a/bfd/libecoff.h b/bfd/libecoff.h
index 1d781c7d309..25619b7346c 100644
--- a/bfd/libecoff.h
+++ b/bfd/libecoff.h
@@ -153,9 +153,6 @@ typedef struct ecoff_tdata
      particular ECOFF file.  This is not valid until
      ecoff_compute_section_file_positions is called.  */
   bool rdata_in_text;
-
-  /* Used by coff-mips.c to track REFHI relocs for pairing with REFLO.  */
-  struct mips_hi *mips_refhi_list;
 } ecoff_data_type;
 
 /* Each canonical asymbol really looks like this.  */
@@ -201,10 +198,13 @@ struct ecoff_section_tdata
      we need to keep track of the gp values that we picked for each
      input .lita section . */
   bfd_vma gp;
+
+  /* Used by coff-mips.c to track REFHI relocs for pairing with REFLO.  */
+  struct mips_hi *mips_refhi_list;
 };
 
 /* An accessor macro for the ecoff_section_tdata structure.  */
-#define ecoff_section_data(abfd, sec) \
+#define ecoff_section_data(sec) \
   ((struct ecoff_section_tdata *) (sec)->used_by_bfd)
 
 /* ECOFF linker hash table entries.  */

-- 
Alan Modra


More information about the Binutils mailing list