diff --git a/NEWS b/NEWS index 5ab1602dbd69..2ed5aef45c61 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ PHP NEWS - Opcache: . Fixed opcache.protect_memory race under ZTS. (realFlowControl) +- PDO: + . Fixed persistent connections with a colon in the username or password + reusing another connection's handle. (iliaal) + - Standard: . Fixed a memory leak in array_merge_recursive() when the recursive merge of an object converted to an array fails. (David Carlier) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 782639be0758..be077e6f28ca 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -390,16 +390,18 @@ PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zen if (Z_TYPE_P(v) == IS_STRING && !is_numeric_string(Z_STRVAL_P(v), Z_STRLEN_P(v), NULL, NULL, 0) && Z_STRLEN_P(v) > 0) { /* user specified key */ - plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", data_source, - username ? username : "", - password ? password : "", - Z_STRVAL_P(v)); + plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%zu:%s:%zu:%s:%zu:%s:%zu:%s", + strlen(data_source), data_source, + username ? strlen(username) : 0, username ? username : "", + password ? strlen(password) : 0, password ? password : "", + Z_STRLEN_P(v), Z_STRVAL_P(v)); is_persistent = 1; } else { is_persistent = zval_get_long(v) ? 1 : 0; - plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s", data_source, - username ? username : "", - password ? password : ""); + plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%zu:%s:%zu:%s:%zu:%s", + strlen(data_source), data_source, + username ? strlen(username) : 0, username ? username : "", + password ? strlen(password) : 0, password ? password : ""); } } diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_persistent_hash_key.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_persistent_hash_key.phpt new file mode 100644 index 000000000000..aabc8ed9e0cd --- /dev/null +++ b/ext/pdo_sqlite/tests/pdo_sqlite_persistent_hash_key.phpt @@ -0,0 +1,39 @@ +--TEST-- +PDO persistent hash key does not collide on colons in credentials +--EXTENSIONS-- +pdo_sqlite +--FILE-- + $persist1]); + $db1->exec("CREATE TABLE {$table} (id INT)"); + unset($db1); + $db2 = new PDO('sqlite::memory:', $user2, $pass2, [PDO::ATTR_PERSISTENT => $persist2]); + return (bool) $db2->query("SELECT 1 FROM sqlite_master WHERE name='{$table}'")->fetchColumn(); +} + +echo "user/pass colon collision: "; +var_dump(persistent_shares('admin', 's:ecret', 'admin:s', 'ecret', true, true, 't_userpass')); + +echo "custom key colon collision: "; +var_dump(persistent_shares('cku', 'b', 'cku', 'b:c', 'c:d', 'd', 't_customkey')); + +echo "same credentials reuse: "; +var_dump(persistent_shares('same', 'creds', 'same', 'creds', true, true, 't_same')); + +echo "distinct credentials: "; +var_dump(persistent_shares('alice', 'secret', 'bob', 'secret', true, true, 't_distinct')); +?> +--EXPECT-- +user/pass colon collision: bool(false) +custom key colon collision: bool(false) +same credentials reuse: bool(true) +distinct credentials: bool(false)