Hi all,

I tried something a little different. This patch regression tested on x86_64. The original bug report dates from 2019.

The patch fixes the issue found with -fsanitizer=address.

OK for mainline? Looks like it ought to be backported after a brief wait.

Regards,

Jerry
---
When an assumed-rank type(T) dummy argument was passed to an assumed-rank
class(T) dummy argument, gfortran generated a GFC_MAX_DIMENSIONS static
struct copy of the array descriptor's dim[] array.  The caller only
allocates storage for dtype.rank dimensions, so the copy read past the
physical end of the descriptor causing a stack-buffer-overflow detected
by AddressSanitizer.

Fix by checking expr->rank == -1 (assumed-rank actual) at the two copy
sites and replacing the static copy with a runtime-sized __builtin_memcpy
of dtype.rank * sizeof(descriptor_dimension) bytes.

Assisted-By: Claude Sonnet 4.6

        PR fortran/60576

gcc/fortran/ChangeLog:

        PR fortran/60576
        * trans-array.cc (gfc_conv_array_parameter): For an assumed-rank
        actual argument passed to a CLASS assumed-rank formal, use a
        runtime-sized memcpy for the dim[] entries instead of a full
        GFC_MAX_DIMENSIONS static copy.
        * trans-expr.cc (gfc_conv_derived_to_class): Likewise for the
        derived_array descriptor copy.

gcc/testsuite/ChangeLog:

        PR fortran/60576
        * gfortran.dg/assumed_rank_26.f90: New test.
---
From f4ce7c377b0438fbd9c64a70dad6595b5704690c Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Tue, 2 Jun 2026 18:17:13 -0700
Subject: [PATCH] Fortran: fix stack overflow passing assumed-rank type to
 assumed-rank class [PR60576]

When an assumed-rank type(T) dummy argument was passed to an assumed-rank
class(T) dummy argument, gfortran generated a GFC_MAX_DIMENSIONS static
struct copy of the array descriptor's dim[] array.  The caller only
allocates storage for dtype.rank dimensions, so the copy read past the
physical end of the descriptor causing a stack-buffer-overflow detected
by AddressSanitizer.

Fix by checking expr->rank == -1 (assumed-rank actual) at the two copy
sites and replacing the static copy with a runtime-sized __builtin_memcpy
of dtype.rank * sizeof(descriptor_dimension) bytes.

Assisted by: Claude Sonnet 4.6

	PR fortran/60576

gcc/fortran/ChangeLog:

	PR fortran/60576
	* trans-array.cc (gfc_conv_array_parameter): For an assumed-rank
	actual argument passed to a CLASS assumed-rank formal, use a
	runtime-sized memcpy for the dim[] entries instead of a full
	GFC_MAX_DIMENSIONS static copy.
	* trans-expr.cc (gfc_conv_derived_to_class): Likewise for the
	derived_array descriptor copy.

gcc/testsuite/ChangeLog:

	PR fortran/60576
	* gfortran.dg/asan/assumed_rank_26.f90: New test.
---
 gcc/fortran/trans-array.cc                    | 32 ++++++++++++++-
 gcc/fortran/trans-expr.cc                     | 34 +++++++++++++++-
 .../gfortran.dg/asan/assumed_rank_26.f90      | 39 +++++++++++++++++++
 3 files changed, 103 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/asan/assumed_rank_26.f90

diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index cb55e5082d4..91c1cdc1e75 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -9505,7 +9505,37 @@ gfc_conv_array_parameter (gfc_se *se, gfc_expr *expr, bool g77,
 	      gfc_conv_descriptor_offset_set (&block, arr, gfc_index_zero_node);
 	      se->expr = arr;
 	    }
-	  gfc_class_array_data_assign (&block, tmp, se->expr, true);
+	  if (expr->rank == -1)
+	    {
+	      /* Assumed-rank actual argument: the caller only allocates storage
+		 for dtype.rank dimensions.  Copying GFC_MAX_DIMENSIONS dim
+		 entries would read past the physical end of the descriptor.
+		 Copy the header fields explicitly and use a runtime-sized
+		 memcpy for the dim[] entries.  PR fortran/60576.  */
+	      tree rank, dim_field, dim_size, copy_size, dst_ptr, src_ptr;
+
+	      gfc_conv_descriptor_data_set (&block, tmp,
+					    gfc_conv_descriptor_data_get (se->expr));
+	      gfc_conv_descriptor_offset_set (&block, tmp,
+					      gfc_conv_descriptor_offset_get (se->expr));
+	      gfc_add_modify (&block, gfc_conv_descriptor_dtype (tmp),
+			      gfc_conv_descriptor_dtype (se->expr));
+	      rank = fold_convert (size_type_node,
+				   gfc_conv_descriptor_rank (se->expr));
+	      dim_field = gfc_get_descriptor_dimension (se->expr);
+	      dim_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (dim_field)));
+	      copy_size = fold_build2_loc (input_location, MULT_EXPR,
+					   size_type_node, rank, dim_size);
+	      dst_ptr = gfc_build_addr_expr (pvoid_type_node,
+					     gfc_get_descriptor_dimension (tmp));
+	      src_ptr = gfc_build_addr_expr (pvoid_type_node, dim_field);
+	      gfc_add_expr_to_block (&block,
+		  build_call_expr_loc (input_location,
+				       builtin_decl_explicit (BUILT_IN_MEMCPY),
+				       3, dst_ptr, src_ptr, copy_size));
+	    }
+	  else
+	    gfc_class_array_data_assign (&block, tmp, se->expr, true);
 
 	  /* Handle optional.  */
 	  if (fsym && fsym->attr.optional && sym && sym->attr.optional)
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 8acee12e9c2..d56ad33dc1f 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -1028,7 +1028,39 @@ gfc_conv_derived_to_class (gfc_se *parmse, gfc_expr *e, gfc_symbol *fsym,
 	    {
 	      *derived_array
 		= gfc_create_var (TREE_TYPE (parmse->expr), "array");
-	      gfc_add_modify (&block, *derived_array, parmse->expr);
+	      if (e->rank == -1)
+		{
+		  /* Assumed-rank actual: parmse->expr physically holds only
+		     dtype.rank dims; a full struct assign reads past the end.
+		     Copy field-by-field with a runtime-sized dim[] memcpy.
+		     PR fortran/60576.  */
+		  tree rank, dim_field, dim_size, copy_size, dst_ptr, src_ptr;
+
+		  gfc_conv_descriptor_data_set
+		    (&block, *derived_array,
+		     gfc_conv_descriptor_data_get (parmse->expr));
+		  gfc_conv_descriptor_offset_set
+		    (&block, *derived_array,
+		     gfc_conv_descriptor_offset_get (parmse->expr));
+		  gfc_add_modify (&block,
+				  gfc_conv_descriptor_dtype (*derived_array),
+				  gfc_conv_descriptor_dtype (parmse->expr));
+		  rank = fold_convert (size_type_node,
+				       gfc_conv_descriptor_rank (parmse->expr));
+		  dim_field = gfc_get_descriptor_dimension (parmse->expr);
+		  dim_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (dim_field)));
+		  copy_size = fold_build2_loc (input_location, MULT_EXPR,
+					       size_type_node, rank, dim_size);
+		  dst_ptr = gfc_build_addr_expr
+		    (pvoid_type_node, gfc_get_descriptor_dimension (*derived_array));
+		  src_ptr = gfc_build_addr_expr (pvoid_type_node, dim_field);
+		  gfc_add_expr_to_block (&block,
+		      build_call_expr_loc (input_location,
+					   builtin_decl_explicit (BUILT_IN_MEMCPY),
+					   3, dst_ptr, src_ptr, copy_size));
+		}
+	      else
+		gfc_add_modify (&block, *derived_array, parmse->expr);
 	    }
 
 	  if (optional)
diff --git a/gcc/testsuite/gfortran.dg/asan/assumed_rank_26.f90 b/gcc/testsuite/gfortran.dg/asan/assumed_rank_26.f90
new file mode 100644
index 00000000000..d2a0e10a047
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/asan/assumed_rank_26.f90
@@ -0,0 +1,39 @@
+! { dg-do run }
+!
+! PR fortran/60576
+!
+! Stack buffer overflow when passing an assumed-rank type(T) dummy argument
+! to an assumed-rank class(T) dummy argument.  The caller only allocates
+! storage for dtype.rank dimensions in the descriptor; generating a full
+! GFC_MAX_DIMENSIONS copy caused a stack-buffer-overflow detected by ASan.
+
+implicit none
+type t
+  integer :: i
+end type
+
+type(T) :: at(2:3,2:4)
+integer :: i = 0
+
+call bar(at)
+if (i /= 2) STOP 1
+
+contains
+  subroutine bar(x)
+    type(t) :: x(..)
+    if (lbound(x,1) /= 1 .or. lbound(x,2) /= 1) STOP 2
+    if (size(x) /= 6) STOP 3
+    if (size(x,1) /= 2 .or. size(x,2) /= 3) STOP 4
+    if (ubound(x,1) /= 2 .or. ubound(x,2) /= 3) STOP 5
+    i = i + 1
+    call foo(x)
+  end subroutine
+  subroutine foo(x)
+    class(t) :: x(..)
+    if (lbound(x,1) /= 1 .or. lbound(x,2) /= 1) STOP 6
+    if (size(x) /= 6) STOP 7
+    if (size(x,1) /= 2 .or. size(x,2) /= 3) STOP 8
+    if (ubound(x,1) /= 2 .or. ubound(x,2) /= 3) STOP 9
+    i = i + 1
+  end subroutine
+end
-- 
2.54.0

Reply via email to