PATCH] Fortran: fix stack overflow passing assumed-rank type to, assumed-rank class [PR60576]

Message ID 0e924538-e692-4ff2-b262-6d54234eca0f@gmail.com
State New
Headers
Series PATCH] Fortran: fix stack overflow passing assumed-rank type to, assumed-rank class [PR60576] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap fail Patch failed to apply

Commit Message

Jerry D June 3, 2026, 1:31 a.m. UTC
  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.
---
  

Comments

Paul Richard Thomas June 4, 2026, 6:54 a.m. UTC | #1
Hello Jerry,

Contrary to my remark on Mattermost, this looks good to me except that
I would halve the size of the patch by refactoring it as in the
attached version. It passes regression testing like the original.

OK for mainline and backporting later on.

Thanks for re-examining this Golden Oldie!

Paul

On Wed, 3 Jun 2026 at 02:32, Jerry D <jvdelisle2@gmail.com> wrote:
>
> 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.
> ---
  
Jerry D June 4, 2026, 7:52 p.m. UTC | #2
On 6/3/26 11:54 PM, Paul Richard Thomas wrote:
> Hello Jerry,
> 
> Contrary to my remark on Mattermost, this looks good to me except that
> I would halve the size of the patch by refactoring it as in the
> attached version. It passes regression testing like the original.
> 
> OK for mainline and backporting later on.
> 
> Thanks for re-examining this Golden Oldie!
> 
> Paul

Paul, the diff you attached threw claude into a tizzy. I slapped it and set it 
back on track. The attached fixes the breakage in the original 
assumed_rank_7.f90 with -fsanitize=address, passes the new 
asan/assumed_rank_26.f90 and fixed a breakage in coarray/dummy_3.f90.

I have completely regression tested this one and manually tested the original 
assume_rank_7.f90 with -fsanitize=address. All pass.

I plan to commit this shortly.

Regards,

Jerry
  
Jerry D June 4, 2026, 7:58 p.m. UTC | #3
The master branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:eaf4292c83804bd21b920db174401ca3702601d4

commit r17-1352-geaf4292c83804bd21b920db174401ca3702601d4
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Thu Jun 4 12:11:37 2026 -0700

Fixed on trunk. I will let it settle a bit before trying to backport.

Thanks to Harald and Paul for reviews.

Jerry
On 6/4/26 12:52 PM, Jerry D wrote:
> On 6/3/26 11:54 PM, Paul Richard Thomas wrote:
>> Hello Jerry,
>>
>> Contrary to my remark on Mattermost, this looks good to me except that
>> I would halve the size of the patch by refactoring it as in the
>> attached version. It passes regression testing like the original.
>>
>> OK for mainline and backporting later on.
>>
>> Thanks for re-examining this Golden Oldie!
>>
>> Paul
> 
> Paul, the diff you attached threw claude into a tizzy. I slapped it and set it 
> back on track. The attached fixes the breakage in the original 
> assumed_rank_7.f90 with -fsanitize=address, passes the new asan/ 
> assumed_rank_26.f90 and fixed a breakage in coarray/dummy_3.f90.
> 
> I have completely regression tested this one and manually tested the original 
> assume_rank_7.f90 with -fsanitize=address. All pass.
> 
> I plan to commit this shortly.
> 
> Regards,
> 
> Jerry
  
Jerry D June 5, 2026, 4:32 p.m. UTC | #4
Paul, take the refactor off your list of TODOs. Its done.

Regression tested before the push.

The master branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:04c3c1b5b2391e33c460d45d22cfa046121c0361

commit r17-1377-g04c3c1b5b2391e33c460d45d22cfa046121c0361
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Fri Jun 5 09:22:55 2026 -0700

     fortran: Refactor the commit for 60576

             PR fortran/60576

     gcc/fortran/ChangeLog:

             * trans-array.cc (gfc_resize_assumed_rank_dim_field): New
             function extracted from gfc_conv_array_parameter.
             (gfc_conv_array_parameter): Use the new function.

On 6/5/26 6:49 AM, Paul Richard Thomas wrote:
> Hi Jerry,
> 
> I am not sure that I understand why my suggested patch "threw Claude
> into a tizzy", The two chunks that you pushed are identical. My
> version turned one into a function in trans-array.cc, provided a
> prototype and called it from trans-expr.cc, thereby halving the size
> of the patch.
> 
> I have learned now that you really have to take a close look at
> Claude-generated patches. They tend to code and comment bloat. Also,
> take care with respect to pointers to the standard. It seems to
> generate them at random.
> 
> Refactoring your patch is on my todo list and I will apply it after
> you backport.
> 
> Regards
> 
> Paul
> 
> On Thu, 4 Jun 2026 at 20:52, Jerry D <jvdelisle2@gmail.com> wrote:
>>
>> On 6/3/26 11:54 PM, Paul Richard Thomas wrote:
>>> Hello Jerry,
>>>
>>> Contrary to my remark on Mattermost, this looks good to me except that
>>> I would halve the size of the patch by refactoring it as in the
>>> attached version. It passes regression testing like the original.
>>>
>>> OK for mainline and backporting later on.
>>>
>>> Thanks for re-examining this Golden Oldie!
>>>
>>> Paul
>>
>> Paul, the diff you attached threw claude into a tizzy. I slapped it and set it
>> back on track. The attached fixes the breakage in the original
>> assumed_rank_7.f90 with -fsanitize=address, passes the new
>> asan/assumed_rank_26.f90 and fixed a breakage in coarray/dummy_3.f90.
>>
>> I have completely regression tested this one and manually tested the original
>> assume_rank_7.f90 with -fsanitize=address. All pass.
>>
>> I plan to commit this shortly.
>>
>> Regards,
>>
>> Jerry
  

Patch

From f4ce7c377b0438fbd9c64a70dad6595b5704690c Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <jvdelisle@gcc.gnu.org>
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