Commit 34116adc119 for php.net

commit 34116adc119f9ca496e7c8f4c1da9b80345f9298
Author: Weilin Du <weilindu@php.net>
Date:   Wed Jun 24 18:42:28 2026 +0800

    Fix GH-9945: Reject shmop/sysvshm keys outside key_t range (#22423)

    Now, shmop_open() and shm_attach() accepted zend_long keys and passed them directly to SysV shared memory APIs, where they may be truncated to key_t.

    Generally we should throw ValurErrors on those issues. Fixes #9945

diff --git a/NEWS b/NEWS
index 231e1788bbe..fede5aa5173 100644
--- a/NEWS
+++ b/NEWS
@@ -206,6 +206,10 @@ PHP                                                                        NEWS
   . Changed defaults of session.use_strict_mode (now 1), session.cookie_httponly
     (now 1) and session.cookie_samesite (now "Lax"). (jorgsowa)

+- Shmop:
+  . Fixed bug GH-9945 (shmop_open() silently truncates keys outside the key_t
+    range). (Weilin Du)
+
 - Soap:
   . Soap::__setCookie() when cookie name is a digit is now not stored and
     represented as a string anymore but a int. (David Carlier)
@@ -236,6 +240,10 @@ PHP                                                                        NEWS
   . Fix bug GH-22062 (SplDoublyLinkedList iterator UAF
     via destructor releasing next node). (David Carlier)

+- Sysvshm:
+  . Fixed shm_attach() to throw ValueError for keys outside the key_t range.
+    (Weilin Du)
+
 - Sqlite3:
   . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)

diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c
index c3ed217443f..645ce172fdc 100644
--- a/ext/shmop/shmop.c
+++ b/ext/shmop/shmop.c
@@ -129,13 +129,20 @@ PHP_MINFO_FUNCTION(shmop)
 /* {{{ gets and attaches a shared memory segment */
 PHP_FUNCTION(shmop_open)
 {
-	zend_long key, mode, size;
+	zend_long key_arg, mode, size;
+	key_t key;
 	php_shmop *shmop;
 	struct shmid_ds shm;
 	char *flags;
 	size_t flags_len;

-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key_arg, &flags, &flags_len, &mode, &size) == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	key = (key_t) key_arg;
+	if ((zend_long) key != key_arg) {
+		zend_argument_value_error(1, "is out of range");
 		RETURN_THROWS();
 	}

diff --git a/ext/shmop/tests/gh9945.phpt b/ext/shmop/tests/gh9945.phpt
new file mode 100644
index 00000000000..f22f79149bf
--- /dev/null
+++ b/ext/shmop/tests/gh9945.phpt
@@ -0,0 +1,19 @@
+--TEST--
+GH-9945: shmop_open() must reject keys outside the key_t range
+--EXTENSIONS--
+shmop
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE !== 8) die('skip only for 64-bit');
+if (PHP_OS_FAMILY !== 'Linux' && PHP_OS_FAMILY !== 'Windows') die('skip only for platforms with 32-bit key_t');
+?>
+--FILE--
+<?php
+try {
+    shmop_open(0x100000000, '', 0644, 1);
+} catch (ValueError $exception) {
+    echo $exception->getMessage(), "\n";
+}
+?>
+--EXPECT--
+shmop_open(): Argument #1 ($key) is out of range
diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c
index 59ca49a974e..6ca06be935d 100644
--- a/ext/sysvshm/sysvshm.c
+++ b/ext/sysvshm/sysvshm.c
@@ -126,10 +126,17 @@ PHP_FUNCTION(shm_attach)
 	sysvshm_shm *shm_list_ptr;
 	char *shm_ptr;
 	sysvshm_chunk_head *chunk_ptr;
-	zend_long shm_key, shm_id, shm_size, shm_flag = 0666;
+	zend_long shm_key_arg, shm_id, shm_size, shm_flag = 0666;
+	key_t shm_key;
 	bool shm_size_is_null = true;

-	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &shm_key, &shm_size, &shm_size_is_null, &shm_flag)) {
+	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &shm_key_arg, &shm_size, &shm_size_is_null, &shm_flag)) {
+		RETURN_THROWS();
+	}
+
+	shm_key = (key_t) shm_key_arg;
+	if ((zend_long) shm_key != shm_key_arg) {
+		zend_argument_value_error(1, "is out of range");
 		RETURN_THROWS();
 	}

@@ -145,17 +152,17 @@ PHP_FUNCTION(shm_attach)
 	/* get the id from a specified key or create new shared memory */
 	if ((shm_id = shmget(shm_key, 0, 0)) < 0) {
 		if (shm_size < (zend_long)sizeof(sysvshm_chunk_head)) {
-			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": memorysize too small", shm_key);
+			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": memorysize too small", shm_key_arg);
 			RETURN_FALSE;
 		}
 		if ((shm_id = shmget(shm_key, shm_size, shm_flag | IPC_CREAT | IPC_EXCL)) < 0) {
-			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
+			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key_arg, strerror(errno));
 			RETURN_FALSE;
 		}
 	}

 	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));
+		php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key_arg, strerror(errno));
 		RETURN_FALSE;
 	}

diff --git a/ext/sysvshm/tests/gh9945.phpt b/ext/sysvshm/tests/gh9945.phpt
new file mode 100644
index 00000000000..8185d39b098
--- /dev/null
+++ b/ext/sysvshm/tests/gh9945.phpt
@@ -0,0 +1,19 @@
+--TEST--
+GH-9945: shm_attach() must reject keys outside the key_t range
+--EXTENSIONS--
+sysvshm
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE !== 8) die('skip only for 64-bit');
+if (PHP_OS_FAMILY !== 'Linux' && PHP_OS_FAMILY !== 'Windows') die('skip only for platforms with 32-bit key_t');
+?>
+--FILE--
+<?php
+try {
+    shm_attach(0x100000000, 0);
+} catch (ValueError $exception) {
+    echo $exception->getMessage(), "\n";
+}
+?>
+--EXPECT--
+shm_attach(): Argument #1 ($key) is out of range