https://gcc.gnu.org/g:0235a09b822088b8bea3bc3fccbea3a8fc773d29

commit r17-5793-g0235a09b822088b8bea3bc3fccbea3a8fc773d29
Author: Jerry DeLisle <[email protected]>
Date:   Wed Oct 28 13:27:21 2026 -0400

    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.

Diff:
---
 .../gfortran.dg/coarray/alloc_pool_exhausted_2.f90 | 25 ++++++++++++++++
 libgfortran/caf/shmem.c                            | 34 +++++++++++++++-------
 libgfortran/caf/shmem/allocator.c                  |  4 +++
 libgfortran/caf/shmem/collective_subroutine.c      | 11 +++++--
 4 files changed, 61 insertions(+), 13 deletions(-)

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 000000000000..28faea352d1a
--- /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 964c7449e028..27aa9e081219 100644
--- a/libgfortran/caf/shmem.c
+++ b/libgfortran/caf/shmem.c
@@ -196,6 +196,15 @@ _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.  The allocator rejects SIZE_MAX,
+   so a saturated size makes the allocation fail through STAT=.  */
+
+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 +266,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 +275,9 @@ _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));
+  /* Saturate like mul_size_sat.  */
+  size = size > SIZE_MAX - sizeof (ptrdiff_t)
+        ? SIZE_MAX : alignto (size, sizeof (ptrdiff_t));
   switch (type)
     {
     case CAF_REGTYPE_LOCK_STATIC:
@@ -281,13 +292,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)
          {
@@ -306,12 +318,13 @@ _gfortran_caf_register (size_t size, caf_register_t type, 
caf_token_t *token,
     case CAF_REGTYPE_EVENT_ALLOC:
       {
        bool created;
+       size_t alloc_size = mul_size_sat (size, local->total_num_images);
 
        allocator_lock (&local->ai.alloc);
-       mem = alloc_get_memory_by_id_created (
-         &local->ai, size * local->total_num_images, next_memid, &created);
+       mem = alloc_get_memory_by_id_created (&local->ai, alloc_size, 
next_memid,
+                                             &created);
        if (created)
-         memset (mem, 0, size * local->total_num_images);
+         memset (mem, 0, alloc_size);
        allocator_unlock (&local->ai.alloc);
       }
       break;
@@ -334,7 +347,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 104a8586052b..ed60869506d6 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 2fef8ae50141..0c91f44a43e5 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;

Reply via email to