Commit 7c6f08945f8 for php.net

commit 7c6f08945f89754f2800e0af1cc2181f639985fe
Author: Petr Sumbera <sumbera@volny.cz>
Date:   Tue Dec 16 14:39:43 2025 +0100

    Improve shared_alloc_shm.c strategy to support OPcache JIT on Solaris

    The SysV shared memory allocator in OPcache hardcodes a maximum segment size of
    32MB (SEG_ALLOC_SIZE_MAX). If the JIT buffer exceeds this, which it does with
    the default 64MB size, startup will fail with "Insufficient shared memory!".

    The allocator will now try allocating a contiguous buffer first, and only then
    use segmentation by searching for continuously smaller powers of 2.

    Fixes GH-20718
    Closes GH-20719

diff --git a/NEWS b/NEWS
index ab6d95471bd..cda0658d87c 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-21097 (Accessing Dom\Node properties can can throw TypeError).
     (ndossche)

+- Opcache:
+  . Fixed bug GH-20718 ("Insufficient shared memory" when using JIT on Solaris).
+    (Petr Sumbera)
+
 - PDO_PGSQL:
   . Fixed bug GH-21055 (connection attribute status typo for GSS negotiation).
     (lsaos)
diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c
index 09a357d189e..b9f8ca4524a 100644
--- a/ext/opcache/shared_alloc_shm.c
+++ b/ext/opcache/shared_alloc_shm.c
@@ -42,7 +42,6 @@
 # define MIN(x, y) ((x) > (y)? (y) : (x))
 #endif

-#define SEG_ALLOC_SIZE_MAX 32*1024*1024
 #define SEG_ALLOC_SIZE_MIN 2*1024*1024

 typedef struct  {
@@ -53,36 +52,38 @@ typedef struct  {
 static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, const char **error_in)
 {
 	int i;
-	size_t allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size;
+	size_t allocate_size = 0, remaining_bytes, seg_allocate_size;
 	int first_segment_id = -1;
 	key_t first_segment_key = -1;
 	struct shmid_ds sds;
 	int shmget_flags;
 	zend_shared_segment_shm *shared_segments;

-	seg_allocate_size = SEG_ALLOC_SIZE_MAX;
-	/* determine segment size we _really_ need:
-	 * no more than to include requested_size
-	 */
-	while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) {
-		seg_allocate_size >>= 1;
-	}
-
 	shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL;

-	/* try allocating this much, if not - try shrinking */
-	while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
-		allocate_size = MIN(requested_size, seg_allocate_size);
-		first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags);
-		if (first_segment_id != -1) {
-			break;
+	/* Try contiguous allocation first. */
+	seg_allocate_size = requested_size;
+	first_segment_id = shmget(first_segment_key, seg_allocate_size, shmget_flags);
+	if (UNEXPECTED(first_segment_id == -1)) {
+		/* Search for biggest n^2 < requested_size. */
+		seg_allocate_size = SEG_ALLOC_SIZE_MIN;
+		while (seg_allocate_size < requested_size / 2) {
+			seg_allocate_size *= 2;
 		}
-		seg_allocate_size >>= 1; /* shrink the allocated block */
-	}

-	if (first_segment_id == -1) {
-		*error_in = "shmget";
-		return ALLOC_FAILURE;
+		/* try allocating this much, if not - try shrinking */
+		while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
+			first_segment_id = shmget(first_segment_key, seg_allocate_size, shmget_flags);
+			if (first_segment_id != -1) {
+				break;
+			}
+			seg_allocate_size >>= 1; /* shrink the allocated block */
+		}
+
+		if (first_segment_id == -1) {
+			*error_in = "shmget";
+			return ALLOC_FAILURE;
+		}
 	}

 	*shared_segments_count = ((requested_size - 1) / seg_allocate_size) + 1;