From 498834ea8a6fd4c54aec146e69ae5abb07780c0c Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 30 Jul 2026 16:37:15 -0400 Subject: [PATCH] sysvmsg/sysvsem: fix IPC_PRIVATE and out-of-range key handling msgget() always creates a queue for IPC_PRIVATE, so the existence probe in msg_queue_exists() leaks one per call, and the same probe in msg_get_queue() shadows the IPC_CREAT branch and hands back a queue with no permission bits. Skip the probe for IPC_PRIVATE. msg_queue_exists(), msg_get_queue() and sem_get() also passed the zend_long key straight to msgget()/semget(), where it truncates to key_t; they now reject out-of-range keys as shmop_open() and shm_attach() have since GH-9945. Closes GH-22956 --- ext/sysvmsg/sysvmsg.c | 34 ++++++++++++++++---- ext/sysvmsg/tests/gh9945.phpt | 26 +++++++++++++++ ext/sysvmsg/tests/msg_queue_ipc_private.phpt | 28 ++++++++++++++++ ext/sysvsem/sysvsem.c | 21 ++++++++---- ext/sysvsem/tests/gh9945.phpt | 19 +++++++++++ 5 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 ext/sysvmsg/tests/gh9945.phpt create mode 100644 ext/sysvmsg/tests/msg_queue_ipc_private.phpt create mode 100644 ext/sysvsem/tests/gh9945.phpt diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index 0c2b3dcf183b..965e6a66fb31 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -192,12 +192,23 @@ PHP_FUNCTION(msg_stat_queue) /* {{{ Check whether a message queue exists */ PHP_FUNCTION(msg_queue_exists) { - zend_long key; + zend_long key_arg; + key_t key; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &key_arg) == FAILURE) { + RETURN_THROWS(); + } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &key) == FAILURE) { + key = (key_t) key_arg; + if ((zend_long) key != key_arg) { + zend_argument_value_error(1, "is out of range"); RETURN_THROWS(); } + if (key == IPC_PRIVATE) { + RETURN_FALSE; + } + RETURN_BOOL(msgget(key, 0) >= 0); } /* }}} */ @@ -205,11 +216,18 @@ PHP_FUNCTION(msg_queue_exists) /* {{{ Attach to a message queue */ PHP_FUNCTION(msg_get_queue) { - zend_long key; + zend_long key_arg; zend_long perms = 0666; + key_t key; sysvmsg_queue_t *mq; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &key, &perms) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &key_arg, &perms) == 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(); } @@ -217,12 +235,16 @@ PHP_FUNCTION(msg_get_queue) mq = Z_SYSVMSG_QUEUE_P(return_value); mq->key = key; - mq->id = msgget(key, 0); + if (key == IPC_PRIVATE) { + mq->id = -1; + } else { + mq->id = msgget(key, 0); + } if (mq->id < 0) { /* doesn't already exist; create it */ mq->id = msgget(key, IPC_CREAT | IPC_EXCL | perms); if (mq->id < 0) { - php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); zval_ptr_dtor(return_value); RETURN_FALSE; } diff --git a/ext/sysvmsg/tests/gh9945.phpt b/ext/sysvmsg/tests/gh9945.phpt new file mode 100644 index 000000000000..e346bde9fc29 --- /dev/null +++ b/ext/sysvmsg/tests/gh9945.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-9945: sysvmsg must reject keys outside the key_t range +--EXTENSIONS-- +sysvmsg +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} + +try { + msg_get_queue(0x100000000); +} catch (ValueError $exception) { + echo $exception::class, ": ", $exception->getMessage(), "\n"; +} +?> +--EXPECT-- +ValueError: msg_queue_exists(): Argument #1 ($key) is out of range +ValueError: msg_get_queue(): Argument #1 ($key) is out of range diff --git a/ext/sysvmsg/tests/msg_queue_ipc_private.phpt b/ext/sysvmsg/tests/msg_queue_ipc_private.phpt new file mode 100644 index 000000000000..951cf62f2ab9 --- /dev/null +++ b/ext/sysvmsg/tests/msg_queue_ipc_private.phpt @@ -0,0 +1,28 @@ +--TEST-- +msg_queue_exists() and msg_get_queue() with IPC_PRIVATE +--EXTENSIONS-- +sysvmsg +--FILE-- + +--EXPECT-- +bool(false) +bool(false) +600 +bool(true) +bool(true) +string(5) "hello" +bool(true) diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index e506bd5bb37c..96efd060273a 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -167,14 +167,21 @@ PHP_MINFO_FUNCTION(sysvsem) /* {{{ Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */ PHP_FUNCTION(sem_get) { - zend_long key, max_acquire = 1, perm = 0666; + zend_long key_arg, max_acquire = 1, perm = 0666; bool auto_release = true; + key_t key; int semid; struct sembuf sop[3]; int count; sysvsem_sem *sem_ptr; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|llb", &key, &max_acquire, &perm, &auto_release)) { + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|llb", &key_arg, &max_acquire, &perm, &auto_release)) { + RETURN_THROWS(); + } + + key = (key_t) key_arg; + if ((zend_long) key != key_arg) { + zend_argument_value_error(1, "is out of range"); RETURN_THROWS(); } @@ -186,7 +193,7 @@ PHP_FUNCTION(sem_get) semid = semget(key, 3, perm|IPC_CREAT); if (semid == -1) { - php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); RETURN_FALSE; } @@ -218,7 +225,7 @@ PHP_FUNCTION(sem_get) sop[2].sem_flg = SEM_UNDO; while (semop(semid, sop, 3) == -1) { if (errno != EINTR) { - php_error_docref(NULL, E_WARNING, "Failed acquiring SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed acquiring SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); break; } } @@ -226,7 +233,7 @@ PHP_FUNCTION(sem_get) /* Get the usage count. */ count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL); if (count == -1) { - php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); } /* If we are the only user, then take this opportunity to set the max. */ @@ -235,7 +242,7 @@ PHP_FUNCTION(sem_get) union semun semarg; semarg.val = max_acquire; if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) { - php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); } } @@ -246,7 +253,7 @@ PHP_FUNCTION(sem_get) sop[0].sem_flg = SEM_UNDO; while (semop(semid, sop, 1) == -1) { if (errno != EINTR) { - php_error_docref(NULL, E_WARNING, "Failed releasing SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno)); + php_error_docref(NULL, E_WARNING, "Failed releasing SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key_arg, strerror(errno)); break; } } diff --git a/ext/sysvsem/tests/gh9945.phpt b/ext/sysvsem/tests/gh9945.phpt new file mode 100644 index 000000000000..a7959eb21563 --- /dev/null +++ b/ext/sysvsem/tests/gh9945.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-9945: sem_get() must reject keys outside the key_t range +--EXTENSIONS-- +sysvsem +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +ValueError: sem_get(): Argument #1 ($key) is out of range