Commit a72e0636fe4 for php.net

commit a72e0636fe423a7b32739d502a0c40582d157fc1
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Thu Jul 30 18:06:02 2026 -0400

    ext/sysvshm: don't orphan the segment shm_attach() just created

    When the key does not exist yet, shm_attach() creates the segment with
    IPC_CREAT | IPC_EXCL and then attaches to it. If shmat() fails the
    segment stays behind with no PHP handle able to remove it, and the
    existence probe finds it on every later call, so a permission mask that
    denies the caller wedges the key until ipcrm. Remove the segment on that
    failure; IPC_EXCL makes the ownership flag exact, so one we opened
    rather than created is never touched.

    Closes GH-22959

diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c
index 3fa7c77add3..84c10add85e 100644
--- a/ext/sysvshm/sysvshm.c
+++ b/ext/sysvshm/sysvshm.c
@@ -132,6 +132,7 @@ PHP_FUNCTION(shm_attach)
 	sysvshm_chunk_head *chunk_ptr;
 	zend_long shm_key, shm_id, shm_size, shm_flag = 0666;
 	bool shm_size_is_null = 1;
+	bool created = false;

 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &shm_key, &shm_size, &shm_size_is_null, &shm_flag)) {
 		RETURN_THROWS();
@@ -156,10 +157,14 @@ PHP_FUNCTION(shm_attach)
 			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
 			RETURN_FALSE;
 		}
+		created = true;
 	}

 	if ((shm_ptr = shmat(shm_id, NULL, 0)) == (void *) -1) {
 		php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
+		if (created) {
+			shmctl(shm_id, IPC_RMID, NULL);
+		}
 		RETURN_FALSE;
 	}

diff --git a/ext/sysvshm/tests/shm_attach_failed_attach.phpt b/ext/sysvshm/tests/shm_attach_failed_attach.phpt
new file mode 100644
index 00000000000..34b3261a246
--- /dev/null
+++ b/ext/sysvshm/tests/shm_attach_failed_attach.phpt
@@ -0,0 +1,34 @@
+--TEST--
+shm_attach() removes the segment it created when shmat() fails
+--EXTENSIONS--
+sysvshm
+posix
+--SKIPIF--
+<?php
+if (posix_geteuid() === 0) die('skip cannot run as root');
+?>
+--FILE--
+<?php
+$key = ftok(__FILE__, 't');
+
+var_dump(shm_attach($key, 1024, 0));
+
+$segment = shm_attach($key, 1024, 0600);
+
+if (!$segment instanceof SysvSharedMemory) {
+    die("the key is still held by the segment of the failed attach\n");
+}
+
+try {
+    var_dump(shm_put_var($segment, 1, 'value'));
+    var_dump(shm_get_var($segment, 1));
+} finally {
+    var_dump(shm_remove($segment));
+}
+?>
+--EXPECTF--
+Warning: shm_attach(): Failed for key 0x%x: %s in %s on line %d
+bool(false)
+bool(true)
+string(5) "value"
+bool(true)