From 0f0a6e85e36432b8c5d6745e785ddee34f3ad343 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Thu, 28 Sep 2017 20:22:10 +0200 Subject: [PATCH] PdoSessionHandler: fix advisory lock for pgsql when session.sid_bits_per_character > 4 --- .../Storage/Handler/PdoSessionHandler.php | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 8909a5f401fdc..483e73a119a04 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -580,11 +580,11 @@ private function doAdvisoryLock($sessionId) return $releaseStmt; case 'pgsql': // Obtaining an exclusive session level advisory lock requires an integer key. - // So we convert the HEX representation of the session id to an integer. - // Since integers are signed, we have to skip one hex char to fit in the range. - if (4 === PHP_INT_SIZE) { - $sessionInt1 = hexdec(substr($sessionId, 0, 7)); - $sessionInt2 = hexdec(substr($sessionId, 7, 7)); + // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters. + // So we cannot just use hexdec(). + if (4 === \PHP_INT_SIZE) { + $sessionInt1 = $this->convertStringToInt($sessionId); + $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4)); $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)'); $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); @@ -595,7 +595,7 @@ private function doAdvisoryLock($sessionId) $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); } else { - $sessionBigInt = hexdec(substr($sessionId, 0, 15)); + $sessionBigInt = $this->convertStringToInt($sessionId); $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)'); $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); @@ -613,6 +613,27 @@ private function doAdvisoryLock($sessionId) } } + /** + * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer. + * + * Keep in mind, PHP integers are signed. + * + * @param string $string + * + * @return int + */ + private function convertStringToInt($string) + { + if (4 === \PHP_INT_SIZE) { + return (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]); + } + + $int1 = (ord($string[7]) << 24) + (ord($string[6]) << 16) + (ord($string[5]) << 8) + ord($string[4]); + $int2 = (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]); + + return $int2 + ($int1 << 32); + } + /** * Return a locking or nonlocking SQL query to read session information. *