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