Commit 4868dd4e020 for php.net

commit 4868dd4e0205658c5397bfb23073151514078cdb
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Aug 24 13:30:00 2026 -0400

    ext/session: Abort strict-mode re-creation when create_sid() fails

    When a save handler rejects the session ID under session.use_strict_mode,
    php_session_initialize() papered over a failing s_create_sid() by generating
    an internal ID and carrying on, so reset_id()'s Set-Cookie and SID side
    effects ran for a session that never started, and a second failure left
    PS(id) NULL. Mirror the primary no-ID branch: abort the session, throw when
    no exception is pending, and return FAILURE. Sibling sites audited,
    session_regenerate_id() and session_create_id() already NULL-check with
    their own error paths.

    Closes GH-23847

diff --git a/NEWS b/NEWS
index 4450776530c..6235628cbb1 100644
--- a/NEWS
+++ b/NEWS
@@ -68,6 +68,10 @@ PHP                                                                        NEWS
   . Fixed a heap over-read in the interactive shell prompt when cli.prompt is
     set to an empty string. (Ilia Alshanetsky)

+- Session:
+  . Fixed session_start() continuing after a failed create_sid() when
+    session.use_strict_mode rejects the supplied ID. (Ilia Alshanetsky)
+
 - Sockets:
   . Fixed socket_select() silently truncating sets larger than FD_SETSIZE on
     Windows. (David Carlier)
diff --git a/ext/session/session.c b/ext/session/session.c
index 2073ea55fe1..bed25c2ebba 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -465,7 +465,11 @@ static zend_result php_session_initialize(void) /* {{{ */
 		}
 		PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
 		if (!PS(id)) {
-			PS(id) = php_session_create_id(NULL);
+			php_session_abort();
+			if (!EG(exception)) {
+				zend_throw_error(NULL, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+			}
+			return FAILURE;
 		}
 		if (PS(use_cookies)) {
 			PS(send_cookie) = 1;
diff --git a/ext/session/tests/user_session_module/session_start_strict_recreate_create_sid_fails.phpt b/ext/session/tests/user_session_module/session_start_strict_recreate_create_sid_fails.phpt
new file mode 100644
index 00000000000..3c3ac1dea32
--- /dev/null
+++ b/ext/session/tests/user_session_module/session_start_strict_recreate_create_sid_fails.phpt
@@ -0,0 +1,49 @@
+--TEST--
+session_start() strict mode re-creation must abort when create_sid fails
+--INI--
+session.use_trans_sid=1
+session.use_only_cookies=0
+error_reporting=E_ALL & ~E_DEPRECATED
+--EXTENSIONS--
+session
+--FILE--
+<?php
+
+class FailingHandler implements SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface
+{
+    public function open($path, $name): bool { return true; }
+    public function close(): bool { return true; }
+    public function read($id): string|false { return ''; }
+    public function write($id, $data): bool { return true; }
+    public function destroy($id): bool { return true; }
+    public function gc($max_lifetime): int|false { return 0; }
+    public function updateTimestamp($id, $data): bool { return true; }
+
+    public function create_sid(): string
+    {
+        throw new RuntimeException('create_sid failed');
+    }
+
+    public function validateId($id): bool
+    {
+        return false;
+    }
+}
+
+session_set_save_handler(new FailingHandler(), true);
+session_id(str_repeat('a', 32));
+
+try {
+    var_dump(session_start(['use_strict_mode' => true]));
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+
+var_dump(session_status() === PHP_SESSION_ACTIVE);
+var_dump(defined('SID'));
+
+?>
+--EXPECT--
+Error: Session id must be a string
+bool(false)
+bool(false)