See attached patch. This is 1 of 2 to fix some loose ends in this effort.

Fairly self explanatory.

Regression tested on x86_64

OK for mainline?

Regards,

Jerry

---

libgfortran: [PR127751] caf_shmem: catch overflowing coarray sizes

caf_register multiplied the per-image size by the number of images in
size_t without checking for overflow.  A large request could wrap to a
small size, succeed with STAT=0, and leave the coarray far too small.
The size arithmetic now saturates at SIZE_MAX, and the allocator rejects
sizes too large to round up to a power of two, so the request fails
through STAT= as an exhausted pool does.

The buffer of a collective reduction had the same overflow on the number
of images; its size now saturates as well, and the failure is reported
through STAT= and ERRMSG= of the collective.

Assisted-by: Claude Opus 5.5

        PR libfortran/127751

libgfortran/ChangeLog:

        * caf/shmem.c (mul_size_sat): New function.
        (_gfortran_caf_register): Use it for all size computations and
        saturate the alignment of the size.
        * caf/shmem/allocator.c (allocator_shared_malloc): Return a null
        pointer for sizes that cannot be rounded up to a power of two.
        * caf/shmem/collective_subroutine.c (collsub_reduce_array):
        Saturate the buffer size on overflow.

gcc/testsuite/ChangeLog:

        * gfortran.dg/coarray/alloc_pool_exhausted_2.f90: New test.
---
From cc94e1b684bd823497c3739be465df8d6ff0e460 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Wed, 28 Oct 2026 13:27:21 -0400
Subject: [PATCH 1/2] libgfortran: [PR127751] caf_shmem: catch overflowing
 coarray sizes

caf_register multiplied the per-image size by the number of images in
size_t without checking for overflow.  A large request could wrap to a
small size, succeed with STAT=0, and leave the coarray far too small.
The size arithmetic now saturates at SIZE_MAX, and the allocator rejects
sizes too large to round up to a power of two, so the request fails
through STAT= as an exhausted pool does.

The buffer of a collective reduction had the same overflow on the number
of images; its size now saturates as well, and the failure is reported
through STAT= and ERRMSG= of the collective.

Assisted-by: Claude Opus 5.5

	PR libfortran/127751

libgfortran/ChangeLog:

	* caf/shmem.c (mul_size_sat): New function.
	(_gfortran_caf_register): Use it for all size computations and
	saturate the alignment of the size.
	* caf/shmem/allocator.c (allocator_shared_malloc): Return a null
	pointer for sizes that cannot be rounded up to a power of two.
	* caf/shmem/collective_subroutine.c (collsub_reduce_array):
	Saturate the buffer size on overflow.

gcc/testsuite/ChangeLog:

	* gfortran.dg/coarray/alloc_pool_exhausted_2.f90: New test.
---
 .../coarray/alloc_pool_exhausted_2.f90        | 25 +++++++++++++++++
 libgfortran/caf/shmem.c                       | 28 +++++++++++++------
 libgfortran/caf/shmem/allocator.c             |  4 +++
 libgfortran/caf/shmem/collective_subroutine.c | 11 ++++++--
 4 files changed, 57 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/coarray/alloc_pool_exhausted_2.f90

diff --git a/gcc/testsuite/gfortran.dg/coarray/alloc_pool_exhausted_2.f90 b/gcc/testsuite/gfortran.dg/coarray/alloc_pool_exhausted_2.f90
new file mode 100644
index 00000000000..28faea352d1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/coarray/alloc_pool_exhausted_2.f90
@@ -0,0 +1,25 @@
+! { dg-do run }
+! PR libfortran/127751
+!
+! ALLOCATE of a coarray whose total size over all images does not fit in
+! size_t reports a nonzero STAT= instead of allocating a wrapped-around size.
+
+program alloc_pool_exhausted_2
+  use iso_c_binding, only: c_intptr_t
+  implicit none
+  ! 2**62 bytes per image on 64-bit targets, 2**30 on 32-bit targets
+  integer(8), parameter :: n = 2_8**(bit_size (0_c_intptr_t) - 5)
+  integer(8), allocatable :: a(:)[:]
+  integer :: st, nb
+
+  ! With four images the total size wraps to zero
+  if (num_images () < 4) stop
+  allocate (a(n)[*], stat=st)
+  if (st == 0) stop 1
+  allocate (a(10)[*], stat=st)
+  if (st /= 0) stop 2
+  a = this_image ()
+  sync all
+  nb = mod (this_image (), num_images ()) + 1
+  if (a(10)[nb] /= nb) stop 3
+end program alloc_pool_exhausted_2
diff --git a/libgfortran/caf/shmem.c b/libgfortran/caf/shmem.c
index 964c7449e02..88b800e5247 100644
--- a/libgfortran/caf/shmem.c
+++ b/libgfortran/caf/shmem.c
@@ -196,6 +196,14 @@ _gfortran_caf_num_images (caf_team_t team, int32_t *team_number)
   return caf_current_team->u.image_info->image_map_size;
 }
 
+/* Return A * B, or SIZE_MAX on overflow.  */
+
+static size_t
+mul_size_sat (size_t a, size_t b)
+{
+  size_t r;
+  return __builtin_mul_overflow (a, b, &r) ? SIZE_MAX : r;
+}
 
 void
 _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
@@ -257,7 +265,7 @@ _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
       break;
     case CAF_REGTYPE_EVENT_ALLOC:
     case CAF_REGTYPE_EVENT_STATIC:
-      size *= sizeof (void *);
+      size = mul_size_sat (size, sizeof (void *));
       break;
     default:
       break;
@@ -266,7 +274,8 @@ _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
   if (type != CAF_REGTYPE_COARRAY_ALLOC_ALLOCATE_ONLY)
     *token = malloc (sizeof (struct caf_shmem_token));
 
-  size = alignto (size, sizeof (ptrdiff_t));
+  size = size > SIZE_MAX - sizeof (ptrdiff_t)
+	 ? SIZE_MAX : alignto (size, sizeof (ptrdiff_t));
   switch (type)
     {
     case CAF_REGTYPE_LOCK_STATIC:
@@ -281,13 +290,14 @@ _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
 #if defined(WIN32) || defined(__CYGWIN__)
 	/* On Windows mutexes are not an object stored in the shmem but
 	   identified by an id.  */
-	alloc_size = size * caf_current_team->u.image_info->image_count.count;
+	alloc_size = mul_size_sat (
+	  size, caf_current_team->u.image_info->image_count.count);
 #else
 	alloc_size = size;
 #endif
-	addr = alloc_get_memory_by_id_created (&local->ai,
-					       alloc_size * sizeof (caf_shmem_lock_t),
-					       next_memid, &created);
+	addr = alloc_get_memory_by_id_created (
+	  &local->ai, mul_size_sat (alloc_size, sizeof (caf_shmem_lock_t)),
+	  next_memid, &created);
 
 	if (created)
 	  {
@@ -309,7 +319,8 @@ _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
 
 	allocator_lock (&local->ai.alloc);
 	mem = alloc_get_memory_by_id_created (
-	  &local->ai, size * local->total_num_images, next_memid, &created);
+	  &local->ai, mul_size_sat (size, local->total_num_images), next_memid,
+	  &created);
 	if (created)
 	  memset (mem, 0, size * local->total_num_images);
 	allocator_unlock (&local->ai.alloc);
@@ -334,7 +345,8 @@ _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
       break;
     default:
       /* Each image's part is at its index among all images.  */
-      mem = alloc_get_memory_by_id (&local->ai, size * local->total_num_images,
+      mem = alloc_get_memory_by_id (&local->ai,
+				    mul_size_sat (size, local->total_num_images),
 				    next_memid);
       break;
     }
diff --git a/libgfortran/caf/shmem/allocator.c b/libgfortran/caf/shmem/allocator.c
index 104a8586052..ed60869506d 100644
--- a/libgfortran/caf/shmem/allocator.c
+++ b/libgfortran/caf/shmem/allocator.c
@@ -91,6 +91,10 @@ allocator_shared_malloc (allocator *a, size_t size)
   size_t act_size;
   int bucket_list_index;
 
+  /* Sizes beyond the largest power of two of size_t cannot be served.  */
+  if (size > (size_t) 1 << (sizeof (size_t) * __CHAR_BIT__ - 1))
+    return SHMPTR_NULL;
+
   sz = next_power_of_two (size);
   act_size = sz > sizeof (bucket) ? sz : sizeof (bucket);
   bucket_list_index = __builtin_clzl (act_size);
diff --git a/libgfortran/caf/shmem/collective_subroutine.c b/libgfortran/caf/shmem/collective_subroutine.c
index 2fef8ae5014..0c91f44a43e 100644
--- a/libgfortran/caf/shmem/collective_subroutine.c
+++ b/libgfortran/caf/shmem/collective_subroutine.c
@@ -362,6 +362,7 @@ collsub_reduce_array (gfc_descriptor_t *desc, int result_image,
   int imoffset;
   index_type elem_size;
   index_type this_image_size_bytes;
+  size_t buf_size;
   void *this_image_buf, *roll_iter, *src_iter;
   ass_op assign;
   const int this_img_id = caf_current_team->index;
@@ -373,9 +374,13 @@ collsub_reduce_array (gfc_descriptor_t *desc, int result_image,
   elem_size = GFC_DESCRIPTOR_SIZE (desc);
   this_image_size_bytes = elem_size * pi.num_elem;
 
-  if (!get_collsub_buf (this_image_size_bytes
-			  * caf_current_team->u.image_info->image_map_size,
-			&buffer))
+  /* Saturate at SIZE_MAX so the allocation fails instead of wrapping.  */
+  if (__builtin_mul_overflow ((size_t) this_image_size_bytes,
+			      (size_t) caf_current_team->u.image_info
+					 ->image_map_size,
+			      &buf_size))
+    buf_size = SIZE_MAX;
+  if (!get_collsub_buf (buf_size, &buffer))
     return collsub_no_memory ();
   this_image_buf = buffer + this_image_size_bytes * this_img_id;
 
-- 
2.56.0

Reply via email to