Commit 0df284a4fcb for php.net

commit 0df284a4fcbb80910d3c427250ef32861917a455
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Wed Jul 29 12:01:44 2026 -0400

    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

diff --git a/ext/session/session.c b/ext/session/session.c
index 6380505ae95..f03813c791d 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 00000000000..b65c0671d94
--- /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)