Commit 34c2d2840ba for php

commit 34c2d2840ba901f4c4d1f7a4574b7c1ff1cc3601
Author: Daniel Scherzer <daniel.e.scherzer+phpf@gmail.com>
Date:   Thu Oct 1 20:05:09 2026 -0700

    ext/openssl: fix use after free when `session_new_cb` callback closes stream

diff --git a/ext/openssl/tests/close_in_session_new_cb_UAF.phpt b/ext/openssl/tests/close_in_session_new_cb_UAF.phpt
new file mode 100644
index 00000000000..1b088f6711f
--- /dev/null
+++ b/ext/openssl/tests/close_in_session_new_cb_UAF.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Closing the stream in the `session_new_cb` callback triggers use after free
+--EXTENSIONS--
+openssl
+--CREDITS--
+Calif.io
+--CLEAN--
+<?php
+
+$certFile = sys_get_temp_dir() . '/php-audit-session-' . getmypid() . '.pem';
+@unlink($certFile);
+?>
+--FILE--
+<?php
+
+$certFile = sys_get_temp_dir() . '/php-audit-session-' . getmypid() . '.pem';
+
+$serverCode = <<<'PHP'
+$context = stream_context_create(['ssl' => [
+    'local_cert' => '%s',
+    'session_cache' => true,
+    'session_id_context' => 'audit-session',
+]]);
+
+$server = stream_socket_server(
+    'tls://127.0.0.1:0',
+    $errno,
+    $errstr,
+    STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+    $context,
+);
+phpt_notify_server_start($server);
+$client = @stream_socket_accept($server, 10);
+if ($client) {
+    fwrite($client, "ticket\n");
+    fclose($client);
+}
+PHP;
+$serverCode = sprintf($serverCode, $certFile);
+
+$clientCode = <<<'PHP'
+$context = stream_context_create(['ssl' => [
+    'verify_peer' => false,
+    'verify_peer_name' => false,
+    'session_new_cb' => function ($stream, $session): void {
+        fclose($stream);
+    },
+]]);
+
+$client = @stream_socket_client(
+    'tls://{{ ADDR }}',
+    $errno,
+    $errstr,
+    10,
+    STREAM_CLIENT_CONNECT,
+    $context,
+);
+if ($client) {
+    @fgets($client);
+}
+PHP;
+
+require 'CertificateGenerator.inc';
+$certificateGenerator = new CertificateGenerator();
+$certificateGenerator->saveNewCertAsFileWithKey('audit_session', $certFile);
+
+require 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
+
+?>
+--EXPECT--
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index c1b586e5e56..3e074afdfa1 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -2243,7 +2243,12 @@ static int php_openssl_session_new_cb(SSL *ssl, SSL_SESSION *session)
 	ZVAL_RES(&args[0], stream->res);
 	php_openssl_session_object_init(&args[1], session);

+	bool originally_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
+	stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
 	zend_call_known_fcc(&sslsock->session_callbacks->new_cb, NULL, 2, args, NULL);
+	if (!originally_no_fclose) {
+		stream->flags ^= PHP_STREAM_FLAG_NO_FCLOSE;
+	}

 	zval_ptr_dtor(&args[1]);