The attached patch fixes the regression found by Rainer on Solaris Spark. It manifests on x86_64 as invalid reads identified by valgrind.

I did confirm the valgrin errors before the patch and that they go away after the patch. This is sufficient reason to commit this. I did not hear back from Rainer yet in the PR, however if we fix it he will see in his regular builds.

Regression tested on x86_64. Valgrind tested.

OK for mainline?

I will consider backporting to 15 if reasonable. Likely this will be a combining of the original fix for 106946 and this one. (easy to do).

Regards,

Jerry

From: Christopher Albert <[email protected]>
Date: Fri, 13 Mar 2026 20:50:07 +0100
Subject: [PATCH] fortran: Fix use-after-free in CLASS component error recovery
 [PR124482]

The error recovery added in r16-8021 (PR106946) freed CLASS container
symbols when removing invalid CLASS components from a derived type.
However, gfc_build_class_symbol reuses existing containers when multiple
components share the same class type and attributes.  Freeing the
container for a failed component also invalidated it for previously
committed components, causing a use-after-free detectable with valgrind
and manifesting as a SEGV on Solaris/SPARC.

Fix by deferring CLASS container cleanup until after all failed
components are unlinked, then freeing the container only if no remaining
component still references it.

gcc/fortran/ChangeLog:

        PR fortran/124482
        * decl.cc (gfc_match_data_decl): Defer CLASS container cleanup
        until after all failed components are unlinked.  Check remaining
        component list before freeing a shared container.

Signed-off-by: Christopher Albert <[email protected]>
From d08632545837e600f290662c4ca5714227abb647 Mon Sep 17 00:00:00 2001
From: Christopher Albert <[email protected]>
Date: Fri, 13 Mar 2026 20:50:07 +0100
Subject: [PATCH] fortran: Fix use-after-free in CLASS component error recovery
 [PR124482]

The error recovery added in r16-8021 (PR106946) freed CLASS container
symbols when removing invalid CLASS components from a derived type.
However, gfc_build_class_symbol reuses existing containers when multiple
components share the same class type and attributes.  Freeing the
container for a failed component also invalidated it for previously
committed components, causing a use-after-free detectable with valgrind
and manifesting as a SEGV on Solaris/SPARC.

Fix by deferring CLASS container cleanup until after all failed
components are unlinked, then freeing the container only if no remaining
component still references it.

gcc/fortran/ChangeLog:

	PR fortran/124482
	* decl.cc (gfc_match_data_decl): Defer CLASS container cleanup
	until after all failed components are unlinked.  Check remaining
	component list before freeing a shared container.

Signed-off-by: Christopher Albert <[email protected]>
---
 gcc/fortran/decl.cc | 51 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index 551ce86d475..d8aa7d1c06f 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -6994,7 +6994,13 @@ cleanup:
      later calls gfc_undo_symbols, the declaration state is rolled back but
      that helper symbol survives and leaves the component dangling.  Ordinary
      components do not create that extra helper symbol, so leave them in
-     place for the usual follow-up diagnostics.  PR106946.  */
+     place for the usual follow-up diagnostics.  PR106946.
+
+     CLASS containers are shared between components of the same class type
+     and attributes (gfc_build_class_symbol reuses existing containers).
+     We must not free a container that is still referenced by a previously
+     committed component.  Unlink and free the components first, then clean
+     up only orphaned containers.  PR124482.  */
   if (m == MATCH_ERROR && gfc_comp_struct (gfc_current_state ()))
     {
       gfc_symbol *block = gfc_current_block ();
@@ -7006,27 +7012,52 @@ cleanup:
 	  else
 	    prev = &block->components;
 
+	  /* Record the CLASS container from the removed components.
+	     Normally all components in one declaration share a single
+	     container, but per-variable array specs can produce
+	     additional ones; any beyond the first are harmlessly
+	     leaked until namespace destruction.  */
+	  gfc_symbol *fclass_container = NULL;
+
 	  while (*prev)
 	    {
 	      gfc_component *c = *prev;
 	      if (c->ts.type == BT_CLASS && c->ts.u.derived
 		  && c->ts.u.derived->attr.is_class)
 		{
-		  /* Unlink this CLASS component.  */
 		  *prev = c->next;
-
-		  /* Remove the CLASS container from the namespace.  */
-		  gfc_symbol *fclass = c->ts.u.derived;
-		  if (gfc_find_symtree (fclass->ns->sym_root, fclass->name))
-		    gfc_delete_symtree (&fclass->ns->sym_root, fclass->name);
-		  gfc_release_symbol (fclass);
-
-		  /* Free the component structure.  */
+		  if (!fclass_container)
+		    fclass_container = c->ts.u.derived;
+		  c->ts.u.derived = NULL;
 		  gfc_free_component (c);
 		}
 	      else
 		prev = &c->next;
 	    }
+
+	  /* Free the container only if no remaining component still
+	     references it.  CLASS containers are shared between
+	     components of the same class type and attributes
+	     (gfc_build_class_symbol reuses existing ones).  */
+	  if (fclass_container)
+	    {
+	      bool shared = false;
+	      for (gfc_component *q = block->components; q; q = q->next)
+		if (q->ts.type == BT_CLASS
+		    && q->ts.u.derived == fclass_container)
+		  {
+		    shared = true;
+		    break;
+		  }
+	      if (!shared)
+		{
+		  if (gfc_find_symtree (fclass_container->ns->sym_root,
+					fclass_container->name))
+		    gfc_delete_symtree (&fclass_container->ns->sym_root,
+					fclass_container->name);
+		  gfc_release_symbol (fclass_container);
+		}
+	    }
 	}
     }
 
-- 
2.53.0

Reply via email to