public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Remi Collet <remi@fedoraproject.org>
To: git-commits@fedoraproject.org
Subject: [rpms/php-pecl-mongodb] epel9: Reject null bytes in namespaces and periods in database names
Date: Tue, 08 Sep 2026 05:26:17 GMT	[thread overview]
Message-ID: <178884517778.1.12684466697886281762.rpms-php-pecl-mongodb-7224075a92ad@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/php-pecl-mongodb
            Branch : epel9
            Commit : 7224075a92adcb04164755e3e11dea83abfb9813
            Author : Remi Collet <remi@fedoraproject.org>
            Date   : 2026-09-08T07:26:08+02:00
            Stats  : +656/-1 in 3 file(s)
            URL    : https://src.fedoraproject.org/rpms/php-pecl-mongodb/c/7224075a92adcb04164755e3e11dea83abfb9813?branch=epel9

            Log:
            Reject null bytes in namespaces and periods in database names

Fix out-of-bounds read when building BSON field path
  CVE-2026-84968

---
diff --git a/mongodb-phpc-2744.patch b/mongodb-phpc-2744.patch
new file mode 100644
index 0000000..f98b8fa
--- /dev/null
+++ b/mongodb-phpc-2744.patch
@@ -0,0 +1,109 @@
+From 45010450af47c02668e9771cfe458c02986872cd Mon Sep 17 00:00:00 2001
+From: Pauline Vos <pauline.vos@mongodb.com>
+Date: Thu, 3 Sep 2026 18:08:25 +0200
+Subject: [PATCH] PHPC-2744: Fix dependency issue (#2081)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Co-authored-by: Jérôme Tamarelle <jerome@tamarelle.net>
+---
+ src/phongo_bson.c             | 16 +++++++++---
+ tests/bson/bug-phpc-2744.phpt | 47 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 60 insertions(+), 3 deletions(-)
+ create mode 100644 tests/bson/bug-phpc-2744.phpt
+
+diff --git a/src/phongo_bson.c b/src/phongo_bson.c
+index 3f3a52ae2..75787052b 100644
+--- a/src/phongo_bson.c
++++ b/src/phongo_bson.c
+@@ -87,7 +87,12 @@ char* php_phongo_field_path_as_string(php_phongo_field_path* field_path)
+ 		return estrdup("");
+ 	}
+ 
+-	for (i = 0; i <= field_path->size; i++) {
++	/* Iterate up to and including "size", since the element for the current
++	 * level is stored at that index and is only counted once the level is
++	 * pushed. That index is not always allocated: the array only grows when an
++	 * element is written to it, so stop at "allocated_size" to avoid reading
++	 * past the end of the array. */
++	for (i = 0; i <= field_path->size && i < field_path->allocated_size; i++) {
+ 		if (!field_path->elements[i]) {
+ 			continue;
+ 		}
+@@ -97,7 +102,7 @@ char* php_phongo_field_path_as_string(php_phongo_field_path* field_path)
+ 	path = emalloc(length);
+ 	ptr  = path;
+ 
+-	for (i = 0; i <= field_path->size; i++) {
++	for (i = 0; i <= field_path->size && i < field_path->allocated_size; i++) {
+ 		if (!field_path->elements[i]) {
+ 			continue;
+ 		}
+@@ -106,7 +111,12 @@ char* php_phongo_field_path_as_string(php_phongo_field_path* field_path)
+ 		ptr[0] = '.';
+ 		ptr++;
+ 	}
+-	ptr[-1] = '\0';
++
++	if (ptr == path) {
++		path[0] = '\0';
++	} else {
++		ptr[-1] = '\0';
++	}
+ 
+ 	return path;
+ }
+diff --git a/tests/bson/bug-phpc-2744.phpt b/tests/bson/bug-phpc-2744.phpt
+new file mode 100644
+index 000000000..091b6d6bf
+--- /dev/null
++++ b/tests/bson/bug-phpc-2744.phpt
+@@ -0,0 +1,47 @@
++--TEST--
++PHPC-2744: Field path for corrupt BSON at a nesting depth that is a multiple of 8
++--DESCRIPTION--
++The field path element for the current level is stored at index "size", which may
++be past the end of the allocation when the depth is a multiple of the allocation
++step. Reading it leaked heap memory into the exception message.
++--FILE--
++<?php
++
++require_once __DIR__ . '/../utils/basic.inc';
++
++function nest(int $depth): array
++{
++    $value = ['INVALID!' => 'bar'];
++
++    for ($i = 0; $i < $depth; $i++) {
++        $value = ['a' => $value];
++    }
++
++    return $value;
++}
++
++foreach ([7, 8, 9, 15, 16, 17] as $depth) {
++    $bson = str_replace('INVALID!', "INVALID\xFE", fromPHP(nest($depth)));
++
++    echo throws(function() use ($bson) {
++        MongoDB\BSON\Document::fromBSON($bson)->toPHP();
++    }, MongoDB\Driver\Exception\UnexpectedValueException::class), "\n";
++}
++
++?>
++===DONE===
++<?php exit(0); ?>
++--EXPECT--
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a' at offset 0
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a.a' at offset 0
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a.a.a' at offset 0
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a' at offset 0
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a' at offset 0
++OK: Got MongoDB\Driver\Exception\UnexpectedValueException
++Detected corrupt BSON data for field path 'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a' at offset 0
++===DONE===

diff --git a/mongodb-phpc-2745.patch b/mongodb-phpc-2745.patch
new file mode 100644
index 0000000..8bc3565
--- /dev/null
+++ b/mongodb-phpc-2745.patch
@@ -0,0 +1,537 @@
+From f80ac47bd33f5ffd99257b4fa6a86c42a81c035b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?=
+ <jerome.tamarelle@mongodb.com>
+Date: Thu, 27 Aug 2026 16:32:30 +0200
+Subject: [PATCH] PHPC-2745: Reject null bytes in namespaces and periods in
+ database names (#2059)
+
+---
+ src/MongoDB/ClientEncryption.c                |  8 ++++
+ src/MongoDB/Manager.c                         | 12 ++---
+ src/MongoDB/Server.c                          | 12 ++---
+ src/phongo_client.c                           |  8 ++++
+ src/phongo_execute.c                          | 18 ++++++--
+ src/phongo_execute.h                          |  6 +--
+ src/phongo_util.c                             | 43 ++++++++++++++++++
+ src/phongo_util.h                             |  3 ++
+ .../clientEncryption-ctor_error-002.phpt      |  7 +++
+ .../manager-executeBulkWrite_error-012.phpt   | 36 +++++++++++++++
+ .../manager-executeCommand_error-006.phpt     | 45 +++++++++++++++++++
+ .../manager-executeQuery_error-004.phpt       | 32 +++++++++++++
+ tests/manager/manager-namespace-dots.phpt     | 43 ++++++++++++++++++
+ 13 files changed, 255 insertions(+), 18 deletions(-)
+ create mode 100644 tests/manager/manager-executeBulkWrite_error-012.phpt
+ create mode 100644 tests/manager/manager-executeCommand_error-006.phpt
+ create mode 100644 tests/manager/manager-executeQuery_error-004.phpt
+ create mode 100644 tests/manager/manager-namespace-dots.phpt
+
+diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c
+index c8950d551..dec5f66b7 100644
+--- a/src/MongoDB/ClientEncryption.c
++++ b/src/MongoDB/ClientEncryption.c
+@@ -577,6 +577,14 @@ static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(z
+ 
+ 		key_vault_namespace = php_array_fetchc_string(options, "keyVaultNamespace", &plen, &pfree);
+ 
++		if (!phongo_validate_namespace(key_vault_namespace, plen)) {
++			if (pfree) {
++				efree(key_vault_namespace);
++			}
++
++			goto cleanup;
++		}
++
+ 		if (!phongo_split_namespace(key_vault_namespace, &db_name, &coll_name)) {
+ 			phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"keyVaultNamespace\" option to contain a full collection namespace");
+ 
+diff --git a/src/MongoDB/Manager.c b/src/MongoDB/Manager.c
+index c61a68b9a..175dd59a2 100644
+--- a/src/MongoDB/Manager.c
++++ b/src/MongoDB/Manager.c
+@@ -368,7 +368,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_command(getThis(), PHONGO_COMMAND_RAW, db, command, options, server_id, return_value);
++	phongo_execute_command(getThis(), PHONGO_COMMAND_RAW, db, db_len, command, options, server_id, return_value);
+ 
+ cleanup:
+ 	if (free_options) {
+@@ -417,7 +417,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeReadCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_command(getThis(), PHONGO_COMMAND_READ, db, command, options, server_id, return_value);
++	phongo_execute_command(getThis(), PHONGO_COMMAND_READ, db, db_len, command, options, server_id, return_value);
+ }
+ 
+ /* Execute a WriteCommand */
+@@ -455,7 +455,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeWriteCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_command(getThis(), PHONGO_COMMAND_WRITE, db, command, options, server_id, return_value);
++	phongo_execute_command(getThis(), PHONGO_COMMAND_WRITE, db, db_len, command, options, server_id, return_value);
+ }
+ 
+ /* Execute a ReadWriteCommand */
+@@ -493,7 +493,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeReadWriteCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_command(getThis(), PHONGO_COMMAND_READ_WRITE, db, command, options, server_id, return_value);
++	phongo_execute_command(getThis(), PHONGO_COMMAND_READ_WRITE, db, db_len, command, options, server_id, return_value);
+ }
+ 
+ /* Execute a Query */
+@@ -540,7 +540,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeQuery)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_query(getThis(), namespace, query, options, server_id, return_value);
++	phongo_execute_query(getThis(), namespace, namespace_len, query, options, server_id, return_value);
+ 
+ cleanup:
+ 	if (free_options) {
+@@ -587,7 +587,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWrite)
+ 	 * that its session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern);
+ 
+-	phongo_execute_bulk_write(getThis(), namespace, bulk, options, server_id, return_value);
++	phongo_execute_bulk_write(getThis(), namespace, namespace_len, bulk, options, server_id, return_value);
+ 
+ cleanup:
+ 	if (free_options) {
+diff --git a/src/MongoDB/Server.c b/src/MongoDB/Server.c
+index e500c7b16..f3b66a53c 100644
+--- a/src/MongoDB/Server.c
++++ b/src/MongoDB/Server.c
+@@ -61,7 +61,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_command(&intern->manager, PHONGO_COMMAND_RAW, db, command, options, intern->server_id, return_value);
++	phongo_execute_command(&intern->manager, PHONGO_COMMAND_RAW, db, db_len, command, options, intern->server_id, return_value);
+ 
+ 	if (free_options) {
+ 		php_phongo_prep_legacy_option_free(options);
+@@ -91,7 +91,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeReadCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_command(&intern->manager, PHONGO_COMMAND_READ, db, command, options, intern->server_id, return_value);
++	phongo_execute_command(&intern->manager, PHONGO_COMMAND_READ, db, db_len, command, options, intern->server_id, return_value);
+ }
+ 
+ /* Executes a WriteCommand on this Server */
+@@ -117,7 +117,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeWriteCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_command(&intern->manager, PHONGO_COMMAND_WRITE, db, command, options, intern->server_id, return_value);
++	phongo_execute_command(&intern->manager, PHONGO_COMMAND_WRITE, db, db_len, command, options, intern->server_id, return_value);
+ }
+ 
+ /* Executes a ReadWriteCommand on this Server */
+@@ -143,7 +143,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeReadWriteCommand)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_command(&intern->manager, PHONGO_COMMAND_READ_WRITE, db, command, options, intern->server_id, return_value);
++	phongo_execute_command(&intern->manager, PHONGO_COMMAND_READ_WRITE, db, db_len, command, options, intern->server_id, return_value);
+ }
+ 
+ /* Executes a Query on this Server */
+@@ -172,7 +172,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeQuery)
+ 	 * session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_query(&intern->manager, namespace, query, options, intern->server_id, return_value);
++	phongo_execute_query(&intern->manager, namespace, namespace_len, query, options, intern->server_id, return_value);
+ 
+ 	if (free_options) {
+ 		php_phongo_prep_legacy_option_free(options);
+@@ -208,7 +208,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeBulkWrite)
+ 	 * that its session pool is cleared. */
+ 	PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager));
+ 
+-	phongo_execute_bulk_write(&intern->manager, namespace, bulk, options, intern->server_id, return_value);
++	phongo_execute_bulk_write(&intern->manager, namespace, namespace_len, bulk, options, intern->server_id, return_value);
+ 
+ 	if (free_options) {
+ 		php_phongo_prep_legacy_option_free(options);
+diff --git a/src/phongo_client.c b/src/phongo_client.c
+index 196742bc9..8a9a3a731 100644
+--- a/src/phongo_client.c
++++ b/src/phongo_client.c
+@@ -1331,6 +1331,14 @@ static bool phongo_manager_set_auto_encryption_opts(php_phongo_manager_t* manage
+ 
+ 		key_vault_ns = php_array_fetchc_string(zAutoEncryptionOpts, "keyVaultNamespace", &plen, &pfree);
+ 
++		if (!phongo_validate_namespace(key_vault_ns, plen)) {
++			if (pfree) {
++				efree(key_vault_ns);
++			}
++
++			goto cleanup;
++		}
++
+ 		if (!phongo_split_namespace(key_vault_ns, &db_name, &coll_name)) {
+ 			phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"keyVaultNamespace\" autoEncryption option to contain a full collection namespace");
+ 
+diff --git a/src/phongo_execute.c b/src/phongo_execute.c
+index 848b69edd..582b219e4 100644
+--- a/src/phongo_execute.c
++++ b/src/phongo_execute.c
+@@ -228,7 +228,7 @@ static bool phongo_parse_write_concern(zval* options, bson_t* mongoc_opts, zval*
+ 	return true;
+ }
+ 
+-bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_bulkwrite_t* bulk_write, zval* options, uint32_t server_id, zval* return_value)
++bool phongo_execute_bulk_write(zval* manager, const char* namespace, size_t namespace_len, php_phongo_bulkwrite_t* bulk_write, zval* options, uint32_t server_id, zval* return_value)
+ {
+ 	mongoc_client_t*              client = NULL;
+ 	bson_error_t                  error  = { 0 };
+@@ -247,6 +247,10 @@ bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_
+ 		return false;
+ 	}
+ 
++	if (!phongo_validate_namespace(namespace, namespace_len)) {
++		return false;
++	}
++
+ 	if (!phongo_split_namespace(namespace, &bulk_write->database, &bulk_write->collection)) {
+ 		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s: %s", "Invalid namespace provided", namespace);
+ 		return false;
+@@ -333,7 +337,7 @@ bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_
+ 	return success;
+ }
+ 
+-bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, zval* zcommand, zval* options, uint32_t server_id, zval* return_value)
++bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, size_t db_len, zval* zcommand, zval* options, uint32_t server_id, zval* return_value)
+ {
+ 	mongoc_client_t*            client;
+ 	const php_phongo_command_t* command;
+@@ -352,6 +356,10 @@ bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const
+ 	client  = Z_MANAGER_OBJ_P(manager)->client;
+ 	command = Z_COMMAND_OBJ_P(zcommand);
+ 
++	if (!phongo_validate_dbname(db, db_len)) {
++		goto cleanup;
++	}
++
+ 	if ((type & PHONGO_OPTION_READ_CONCERN) && !phongo_parse_read_concern(options, &opts)) {
+ 		/* Exception should already have been thrown */
+ 		goto cleanup;
+@@ -503,7 +511,7 @@ bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const
+ 	return result;
+ }
+ 
+-bool phongo_execute_query(zval* manager, const char* namespace, zval* zquery, zval* options, uint32_t server_id, zval* return_value)
++bool phongo_execute_query(zval* manager, const char* namespace, size_t namespace_len, zval* zquery, zval* options, uint32_t server_id, zval* return_value)
+ {
+ 	mongoc_client_t*          client;
+ 	const php_phongo_query_t* query;
+@@ -517,6 +525,10 @@ bool phongo_execute_query(zval* manager, const char* namespace, zval* zquery, zv
+ 
+ 	client = Z_MANAGER_OBJ_P(manager)->client;
+ 
++	if (!phongo_validate_namespace(namespace, namespace_len)) {
++		return false;
++	}
++
+ 	if (!phongo_split_namespace(namespace, &dbname, &collname)) {
+ 		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s: %s", "Invalid namespace provided", namespace);
+ 		return false;
+diff --git a/src/phongo_execute.h b/src/phongo_execute.h
+index 47312b9e7..0a02057bb 100644
+--- a/src/phongo_execute.h
++++ b/src/phongo_execute.h
+@@ -36,9 +36,9 @@ typedef enum {
+ 	PHONGO_COMMAND_READ_WRITE     = 0x05,
+ } php_phongo_command_type_t;
+ 
+-bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_bulkwrite_t* bulk_write, zval* zwriteConcern, uint32_t server_id, zval* return_value);
+-bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, zval* zcommand, zval* zreadPreference, uint32_t server_id, zval* return_value);
+-bool phongo_execute_query(zval* manager, const char* namespace, zval* zquery, zval* zreadPreference, uint32_t server_id, zval* return_value);
++bool phongo_execute_bulk_write(zval* manager, const char* namespace, size_t namespace_len, php_phongo_bulkwrite_t* bulk_write, zval* zwriteConcern, uint32_t server_id, zval* return_value);
++bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, size_t db_len, zval* zcommand, zval* zreadPreference, uint32_t server_id, zval* return_value);
++bool phongo_execute_query(zval* manager, const char* namespace, size_t namespace_len, zval* zquery, zval* zreadPreference, uint32_t server_id, zval* return_value);
+ 
+ bool phongo_parse_read_preference(zval* options, zval** zreadPreference);
+ bool phongo_parse_session(zval* options, mongoc_client_t* client, bson_t* mongoc_opts, zval** zsession);
+diff --git a/src/phongo_util.c b/src/phongo_util.c
+index 3c3c639fd..6115591e2 100644
+--- a/src/phongo_util.c
++++ b/src/phongo_util.c
+@@ -19,6 +19,7 @@
+ 
+ #include <php.h>
+ 
++#include "phongo_error.h"
+ #include "phongo_util.h"
+ 
+ const char* php_phongo_bson_type_to_string(bson_type_t type)
+@@ -142,3 +143,45 @@ bool phongo_split_namespace(const char* namespace, char** dbname, char** cname)
+ 
+ 	return true;
+ }
++
++/* Rejects a namespace that contains a NUL byte. A NUL byte would truncate the
++ * database or collection name at the C-string layer before it reaches the
++ * server, silently retargeting the operation. A period is not rejected here: it
++ * separates the database and collection names, and a period is valid in a
++ * collection name. */
++bool phongo_validate_namespace(const char* namespace, size_t namespace_len)
++{
++	if (namespace == NULL) {
++		return true;
++	}
++
++	if (memchr(namespace, '\0', namespace_len) != NULL) {
++		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s", "Invalid namespace provided: namespaces may not contain a null byte");
++		return false;
++	}
++
++	return true;
++}
++
++/* Rejects a database name that contains a NUL byte or a period. A NUL byte
++ * would truncate the name at the C-string layer; a period would be interpreted
++ * by the server as a namespace separator and retarget the command to a
++ * different database. */
++bool phongo_validate_dbname(const char* db, size_t db_len)
++{
++	if (db == NULL) {
++		return true;
++	}
++
++	if (memchr(db, '\0', db_len) != NULL) {
++		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s", "Invalid database name provided: database names may not contain a null byte");
++		return false;
++	}
++
++	if (memchr(db, '.', db_len) != NULL) {
++		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s: %s", "Invalid database name provided: database names may not contain a '.' character", db);
++		return false;
++	}
++
++	return true;
++}
+diff --git a/src/phongo_util.h b/src/phongo_util.h
+index 1d61be4d2..8aa3362cd 100644
+--- a/src/phongo_util.h
++++ b/src/phongo_util.h
+@@ -28,4 +28,7 @@ bool php_phongo_parse_int64(int64_t* retval, const char* data, size_t data_len);
+ 
+ bool phongo_split_namespace(const char* namespace, char** dbname, char** cname);
+ 
++bool phongo_validate_namespace(const char* namespace, size_t namespace_len);
++bool phongo_validate_dbname(const char* db, size_t db_len);
++
+ #endif /* PHONGO_UTIL_H */
+diff --git a/tests/clientEncryption/clientEncryption-ctor_error-002.phpt b/tests/clientEncryption/clientEncryption-ctor_error-002.phpt
+index 34fdb99c2..7f1d5d95d 100644
+--- a/tests/clientEncryption/clientEncryption-ctor_error-002.phpt
++++ b/tests/clientEncryption/clientEncryption-ctor_error-002.phpt
+@@ -21,6 +21,10 @@ $tests = [
+         // keyVaultNamespace requires a valid kmsProviders option
+         'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]],
+     ] + $baseOptions,
++    [
++        'keyVaultNamespace' => "keyvault.data\0keys",
++        'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]],
++    ] + $baseOptions,
+     ['kmsProviders' => 'not_an_array_or_object'] + $baseOptions,
+     ['tlsOptions' => 'not_an_array_or_object'] + $baseOptions,
+ ];
+@@ -56,6 +60,9 @@ Expected "keyVaultClient" option to be MongoDB\Driver\Manager, string given
+ OK: Got MongoDB\Driver\Exception\InvalidArgumentException
+ Expected "keyVaultNamespace" option to contain a full collection namespace
+ 
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid namespace provided: namespaces may not contain a null byte
++
+ OK: Got MongoDB\Driver\Exception\InvalidArgumentException
+ Expected "kmsProviders" option to be an array or object, string given
+ 
+diff --git a/tests/manager/manager-executeBulkWrite_error-012.phpt b/tests/manager/manager-executeBulkWrite_error-012.phpt
+new file mode 100644
+index 000000000..ae8daf1bb
+--- /dev/null
++++ b/tests/manager/manager-executeBulkWrite_error-012.phpt
+@@ -0,0 +1,36 @@
++--TEST--
++MongoDB\Driver\Manager::executeBulkWrite() rejects a null byte in the namespace
++--SKIPIF--
++<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
++<?php skip_if_not_live(); ?>
++--FILE--
++<?php
++
++require_once __DIR__ . "/../utils/basic.inc";
++
++$manager = create_test_manager();
++
++/* A null byte in the collection name would truncate the namespace at the
++ * C-string layer and silently retarget the write. */
++echo throws(function() use ($manager) {
++    $bulk = new MongoDB\Driver\BulkWrite();
++    $bulk->insert(['x' => 1]);
++    $manager->executeBulkWrite("database.col\0lection", $bulk);
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++/* A null byte in the database name is rejected the same way. */
++echo throws(function() use ($manager) {
++    $bulk = new MongoDB\Driver\BulkWrite();
++    $bulk->insert(['x' => 1]);
++    $manager->executeBulkWrite("data\0base.collection", $bulk);
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++?>
++===DONE===
++<?php exit(0); ?>
++--EXPECT--
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid namespace provided: namespaces may not contain a null byte
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid namespace provided: namespaces may not contain a null byte
++===DONE===
+diff --git a/tests/manager/manager-executeCommand_error-006.phpt b/tests/manager/manager-executeCommand_error-006.phpt
+new file mode 100644
+index 000000000..d8843d0c5
+--- /dev/null
++++ b/tests/manager/manager-executeCommand_error-006.phpt
+@@ -0,0 +1,45 @@
++--TEST--
++MongoDB\Driver\Manager::executeCommand() rejects a null byte or period in the database name
++--SKIPIF--
++<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
++<?php skip_if_not_live(); ?>
++--FILE--
++<?php
++
++require_once __DIR__ . "/../utils/basic.inc";
++
++$manager = create_test_manager();
++
++/* A null byte would truncate the database name at the C-string layer. */
++echo throws(function() use ($manager) {
++    $manager->executeCommand("data\0base", new MongoDB\Driver\Command(['ping' => 1]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++/* A period would be read by the server as a namespace separator and retarget
++ * the command to a different database. */
++echo throws(function() use ($manager) {
++    $manager->executeCommand("data.base", new MongoDB\Driver\Command(['ping' => 1]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++/* The same validation applies to executeReadCommand() and executeWriteCommand(). */
++echo throws(function() use ($manager) {
++    $manager->executeReadCommand("data.base", new MongoDB\Driver\Command(['ping' => 1]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++echo throws(function() use ($manager) {
++    $manager->executeWriteCommand("data.base", new MongoDB\Driver\Command(['ping' => 1]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++?>
++===DONE===
++<?php exit(0); ?>
++--EXPECT--
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid database name provided: database names may not contain a null byte
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid database name provided: database names may not contain a '.' character: data.base
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid database name provided: database names may not contain a '.' character: data.base
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid database name provided: database names may not contain a '.' character: data.base
++===DONE===
+diff --git a/tests/manager/manager-executeQuery_error-004.phpt b/tests/manager/manager-executeQuery_error-004.phpt
+new file mode 100644
+index 000000000..34de99f03
+--- /dev/null
++++ b/tests/manager/manager-executeQuery_error-004.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++MongoDB\Driver\Manager::executeQuery() rejects a null byte in the namespace
++--SKIPIF--
++<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
++<?php skip_if_not_live(); ?>
++--FILE--
++<?php
++
++require_once __DIR__ . "/../utils/basic.inc";
++
++$manager = create_test_manager();
++
++/* A null byte in the collection name would truncate the namespace at the
++ * C-string layer and silently retarget the query. */
++echo throws(function() use ($manager) {
++    $manager->executeQuery("database.col\0lection", new MongoDB\Driver\Query([]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++/* A null byte in the database name is rejected the same way. */
++echo throws(function() use ($manager) {
++    $manager->executeQuery("data\0base.collection", new MongoDB\Driver\Query([]));
++}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
++
++?>
++===DONE===
++<?php exit(0); ?>
++--EXPECT--
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid namespace provided: namespaces may not contain a null byte
++OK: Got MongoDB\Driver\Exception\InvalidArgumentException
++Invalid namespace provided: namespaces may not contain a null byte
++===DONE===
+diff --git a/tests/manager/manager-namespace-dots.phpt b/tests/manager/manager-namespace-dots.phpt
+new file mode 100644
+index 000000000..fbddf384f
+--- /dev/null
++++ b/tests/manager/manager-namespace-dots.phpt
+@@ -0,0 +1,43 @@
++--TEST--
++MongoDB\Driver\Manager: a period in the collection name stays valid
++--SKIPIF--
++<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
++<?php skip_if_not_live(); ?>
++<?php skip_if_not_clean(); ?>
++--FILE--
++<?php
++
++require_once __DIR__ . "/../utils/basic.inc";
++
++$manager = create_test_manager();
++
++/* A period separates the database and the collection name at the first
++ * occurrence, so it stays valid inside a collection name. */
++$collectionName = COLLECTION_NAME . '.with.dots';
++$namespace = DATABASE_NAME . '.' . $collectionName;
++
++$bulk = new MongoDB\Driver\BulkWrite();
++$bulk->insert(['_id' => 1]);
++$result = $manager->executeBulkWrite($namespace, $bulk);
++printf("insertedCount: %d\n", $result->getInsertedCount());
++
++/* listCollections confirms the collection was created with the full name,
++ * including the periods, instead of splitting the namespace. */
++$cursor = $manager->executeCommand(DATABASE_NAME, new MongoDB\Driver\Command([
++    'listCollections' => 1,
++    'filter' => ['name' => $collectionName],
++]));
++$collections = $cursor->toArray();
++printf("matched collections: %d\n", count($collections));
++printf("name matches: %s\n", var_export($collections[0]->name === $collectionName, true));
++
++$manager->executeCommand(DATABASE_NAME, new MongoDB\Driver\Command(['drop' => $collectionName]));
++
++?>
++===DONE===
++<?php exit(0); ?>
++--EXPECT--
++insertedCount: 1
++matched collections: 1
++name matches: true
++===DONE===

diff --git a/php-pecl-mongodb.spec b/php-pecl-mongodb.spec
index c7fa6bd..2750f4e 100644
--- a/php-pecl-mongodb.spec
+++ b/php-pecl-mongodb.spec
@@ -32,12 +32,14 @@
 Summary:        MongoDB driver for PHP
 Name:           php-pecl-%{pecl_name}
 Version:        %{upstream_version}%{?upstream_lower}
-Release:        2%{?dist}
+Release:        3%{?dist}
 License:        Apache-2.0
 URL:            https://pecl.php.net/package/%{pecl_name}
 Source0:        https://pecl.php.net/get/%{pecl_name}-%{upstream_version}%{?upstream_prever}.tgz
 
 Patch0:         %{pecl_name}-cve-2026-6811.patch
+Patch1:         %{pecl_name}-phpc-2745.patch
+Patch2:         %{pecl_name}-phpc-2744.patch
 
 BuildRequires:  gcc
 BuildRequires:  php-devel >= 7.4
@@ -72,6 +74,8 @@ sed -e 's/role="test"/role="src"/' \
 
 pushd %{sources}
 %patch -P0 -p1 -b .cve6811
+%patch -P1 -p1 -b .phpc2745
+%patch -P2 -p1 -b .phpc2744
 
 # Check our macro values
 grep CHECK_MODULES config.m4
@@ -191,6 +195,11 @@ cd ../ZTS
 
 
 %changelog
+* Tue Sep  7 2026 Remi Collet <remi@remirepo.net> - 1.20.1-3
+- Reject null bytes in namespaces and periods in database names
+- Fix out-of-bounds read when building BSON field path
+  CVE-2026-84968
+
 * Wed Jun 10 2026 Remi Collet <remi@remirepo.net> - 1.20.1-2
 - Respect libbson nesting limit when parsing PHP objects
   CVE-2026-6811

                 reply	other threads:[~2026-09-08  5:26 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178884517778.1.12684466697886281762.rpms-php-pecl-mongodb-7224075a92ad@fedoraproject.org \
    --to=remi@fedoraproject.org \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox