See attached patch.  The finalization wrapper of a CLASS object
deallocates the allocatable components of a non-allocatable array
component with expressions like ptr2%entries(:)%children.
gfc_trans_deallocate already has a branch for that form, but the
generic allocatable-component branch before it also ran, using the type
of children for the elements of entries, so it freed whatever lay at
the offset of a component of the wrong type.  The fix computes the
test for that form once and skips the generic branch for it; the
special branch already frees all allocatable components of the
elements.

The fix is in gfc_trans_deallocate in trans-stmt.cc.
allocate_with_source_14.f03 counted the bogus free, so its expected
__builtin_free count drops from 29 to 28; finalize_64.f90 is the new
test.

Regression tested on x86_64.

OK for mainline?

Regards,

Jerry

---

fortran: [PR127941] Fix invalid free in CLASS finalization wrapper

Deallocating a CLASS object whose type has a non-allocatable derived
type array component with nested allocatable components freed an
invalid pointer.  For such a component the finalization wrapper
deallocates an expression like ptr%entries(:)%children, and
gfc_trans_deallocate already handles that form by deallocating the
allocatable components of the array elements.  It also ran the generic
allocatable-component cleanup with the type of the last component,
which applied that type's components to the array elements.  The patch
skips the generic cleanup for this form.

Assisted-by: Claude Opus 5.5

        PR fortran/127941

gcc/fortran/ChangeLog:

        * trans-stmt.cc (gfc_trans_deallocate): Do not deallocate the
        allocatable components of the last component's type when the
        expression is a non-allocatable array component generated by
        finalize_component.

gcc/testsuite/ChangeLog:

        * gfortran.dg/allocate_with_source_14.f03: Expect one fewer call
        to __builtin_free.
        * gfortran.dg/finalize_64.f90: New test.
>From eb1c4944e1fb9f745229cbb2ca6cca21b5172aee Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 16 Oct 2026 11:14:56 -0700
Subject: [PATCH] fortran: [PR127941] Fix invalid free in CLASS finalization
 wrapper

Deallocating a CLASS object whose type has a non-allocatable derived
type array component with nested allocatable components freed an
invalid pointer.  For such a component the finalization wrapper
deallocates an expression like ptr%entries(:)%children, and
gfc_trans_deallocate already handles that form by deallocating the
allocatable components of the array elements.  It also ran the generic
allocatable-component cleanup with the type of the last component,
which applied that type's components to the array elements.  The patch
skips the generic cleanup for this form.

Assisted-by: Claude Opus 5.5

	PR fortran/127941

gcc/fortran/ChangeLog:

	* trans-stmt.cc (gfc_trans_deallocate): Do not deallocate the
	allocatable components of the last component's type when the
	expression is a non-allocatable array component generated by
	finalize_component.

gcc/testsuite/ChangeLog:

	* gfortran.dg/allocate_with_source_14.f03: Expect one fewer call
	to __builtin_free.
	* gfortran.dg/finalize_64.f90: New test.
---
 gcc/fortran/trans-stmt.cc                     | 25 +++++-----
 .../gfortran.dg/allocate_with_source_14.f03   |  2 +-
 gcc/testsuite/gfortran.dg/finalize_64.f90     | 49 +++++++++++++++++++
 3 files changed, 64 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/finalize_64.f90

diff --git a/gcc/fortran/trans-stmt.cc b/gcc/fortran/trans-stmt.cc
index 5b20dd3550d..9b8100496ab 100644
--- a/gcc/fortran/trans-stmt.cc
+++ b/gcc/fortran/trans-stmt.cc
@@ -8129,8 +8129,17 @@ gfc_trans_deallocate (gfc_code *code)
       if (expr->rank || is_coarray_array)
 	{
 	  gfc_ref *ref;
-
-	  if (gfc_bt_struct (expr->ts.type)
+	  /* class.cc(finalize_component) generates these, when a
+	     finalizable entity has a non-allocatable derived type array
+	     component, which has allocatable components.  EXPR then has the
+	     type of its last component, not of the array elements.  */
+	  bool fini_comp_array
+	    = (TREE_CODE (se.expr) == COMPONENT_REF
+	       && TREE_CODE (TREE_TYPE (se.expr)) == ARRAY_TYPE
+	       && TREE_CODE (TREE_TYPE (TREE_TYPE (se.expr))) == RECORD_TYPE);
+
+	  if (!fini_comp_array
+	      && gfc_bt_struct (expr->ts.type)
 	      && expr->ts.u.derived->attr.alloc_comp
 	      && !gfc_is_finalizable (expr->ts.u.derived, NULL))
 	    {
@@ -8177,16 +8186,10 @@ gfc_trans_deallocate (gfc_code *code)
 						caf_dtype);
 	      gfc_add_expr_to_block (&se.pre, tmp);
 	    }
-	  else if (TREE_CODE (se.expr) == COMPONENT_REF
-		   && TREE_CODE (TREE_TYPE (se.expr)) == ARRAY_TYPE
-		   && TREE_CODE (TREE_TYPE (TREE_TYPE (se.expr)))
-			== RECORD_TYPE)
+	  else if (fini_comp_array)
 	    {
-	      /* class.cc(finalize_component) generates these, when a
-		 finalizable entity has a non-allocatable derived type array
-		 component, which has allocatable components. Obtain the
-		 derived type of the array and deallocate the allocatable
-		 components. */
+	      /* Obtain the derived type of the array and deallocate the
+		 allocatable components.  */
 	      for (ref = expr->ref; ref; ref = ref->next)
 		{
 		  if (ref->u.c.component->attr.dimension
diff --git a/gcc/testsuite/gfortran.dg/allocate_with_source_14.f03 b/gcc/testsuite/gfortran.dg/allocate_with_source_14.f03
index 36c1245ccdd..71f9068f3ea 100644
--- a/gcc/testsuite/gfortran.dg/allocate_with_source_14.f03
+++ b/gcc/testsuite/gfortran.dg/allocate_with_source_14.f03
@@ -211,4 +211,4 @@ program main
   deallocate(av)
 end program
 ! { dg-final { scan-tree-dump-times "__builtin_malloc" 23 "original" } }
-! { dg-final { scan-tree-dump-times "__builtin_free" 29 "original" } }
+! { dg-final { scan-tree-dump-times "__builtin_free" 28 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/finalize_64.f90 b/gcc/testsuite/gfortran.dg/finalize_64.f90
new file mode 100644
index 00000000000..1ff3c4a34b0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/finalize_64.f90
@@ -0,0 +1,49 @@
+! { dg-do run }
+!
+! PR 127941
+!
+! Deallocating a CLASS object whose type has a non-allocatable derived
+! type array component with nested allocatable components freed an
+! invalid pointer in the finalization wrapper.
+
+module m
+  type leaf
+    real, allocatable :: value
+  end type
+  type item
+    integer :: tag = 1
+    type(leaf), allocatable :: children(:)
+  end type
+  type container
+    type(item) :: entries(1)
+  end type
+  type, extends(container) :: ext
+    type(item) :: more(2,3)
+  end type
+end module
+
+program p
+  use m
+  class(container), allocatable :: x, y(:)
+
+  allocate (x)
+  deallocate (x)
+
+  allocate (x)
+  allocate (x%entries(1)%children(2))
+  allocate (x%entries(1)%children(2)%value, source = 1.0)
+  deallocate (x)
+
+  allocate (ext :: x)
+  select type (x)
+  type is (ext)
+    allocate (x%more(2,3)%children(1))
+    allocate (x%more(2,3)%children(1)%value, source = 2.0)
+  end select
+  deallocate (x)
+
+  allocate (y(3))
+  allocate (y(2)%entries(1)%children(4))
+  allocate (y(2)%entries(1)%children(3)%value, source = 3.0)
+  deallocate (y)
+end program
-- 
2.56.0

Reply via email to