[php-src] master: ext/session: check the created ID before validating it

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-08-11T20:20:20-04:00

Commit: https://github.com/php/php-src/commit/0df284a4fcbb80910d3c427250ef32861917a455
Raw diff: https://github.com/php/php-src/commit/0df284a4fcbb80910d3c427250ef32861917a455.diff

ext/session: check the created ID before validating it

session_create_id() passes the return value of s_create_sid() straight
into s_validate_sid(). A userland create_sid() that throws makes
ps_create_sid_user() return NULL, and ps_validate_sid_user() then reaches
ZVAL_STR_COPY() with a NULL key. Stop the retry loop when no ID was
created and propagate a pending exception instead of reporting a plain
failure. The four s_create_sid() call sites in php_session_initialize()
and session_regenerate_id() already test for NULL.

Closes GH-22924

Changed paths:
  A  ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt
  M  ext/session/session.c


Diff:

diff --git a/ext/session/session.c b/ext/session/session.c
index 6380505ae951..f03813c791d6 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -2508,6 +2508,9 @@ PHP_FUNCTION(session_create_id)
 		int limit = 3;
 		while (limit--) {
 			new_id = PS(mod)->s_create_sid(&PS(mod_data));
+			if (!new_id) {
+				break;
+			}
 			if (!PS(mod)->s_validate_sid || (PS(mod_user_implemented) && Z_ISUNDEF(PS(mod_user_names).ps_validate_sid))) {
 				break;
 			} else {
@@ -2529,6 +2532,9 @@ PHP_FUNCTION(session_create_id)
 		zend_string_release_ex(new_id, 0);
 	} else {
 		smart_str_free(&id);
+		if (EG(exception)) {
+			RETURN_THROWS();
+		}
 		php_error_docref(NULL, E_WARNING, "Failed to create new ID");
 		RETURN_FALSE;
 	}
diff --git a/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt b/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt
new file mode 100644
index 000000000000..b65c0671d940
--- /dev/null
+++ b/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt
@@ -0,0 +1,49 @@
+--TEST--
+session_create_id() when the create_sid handler throws
+--INI--
+session.save_handler=files
+session.name=PHPSESSID
+session.gc_probability=0
+--EXTENSIONS--
+session
+--FILE--
+<?php
+
+ob_start();
+
+class MySessionHandler extends SessionHandler
+{
+    public int $calls = 0;
+
+    public function create_sid(): string
+    {
+        if ($this->calls++ > 0) {
+            throw new Exception('create_sid failed');
+        }
+        return parent::create_sid();
+    }
+
+    public function validateId(string $id): bool
+    {
+        return false;
+    }
+}
+
+session_set_save_handler(new MySessionHandler(), true);
+session_start();
+
+try {
+    session_create_id();
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+    $previous = $e->getPrevious();
+    echo $previous::class, ": ", $previous->getMessage(), PHP_EOL;
+}
+
+var_dump(session_status() === PHP_SESSION_ACTIVE);
+
+?>
+--EXPECT--
+Error: Session id must be a string
+Exception: create_sid failed
+bool(true)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.