Commit f3df6c15be5 for php
commit f3df6c15be5b7f47c7f95973712e37208cda3763
Author: David Carlier <devnexen@gmail.com>
Date: Thu Nov 5 13:50:09 2026 +0000
Fix GH-25172: ext/openssl: session reuse sharing callbacks with persistent stream
Reusing a session onto a pfsockopen() stream shared the parent's request
heap callback structures, which were then freed with the wrong allocator
at shutdown. The reuse is now refused with a warning.
Close GH-25194
diff --git a/NEWS b/NEWS
index f377385b43e..300f44d70dd 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,10 @@ PHP NEWS
or never terminates). (Ilia Alshanetsky)
. Fixed memory leak in JIT ASSIGN_DIM when the assignment fails. (Jorg Sowa)
+- OpenSSL:
+ . Fixed bug GH-25172 (TLS session reuse shares request-lifetime callback
+ structures with persistent streams). (cxxz16, David Carlier)
+
05 Nov 2026, PHP 8.6.0RC5
- Core:
diff --git a/ext/openssl/tests/gh25172.phpt b/ext/openssl/tests/gh25172.phpt
new file mode 100644
index 00000000000..8cd231e1b98
--- /dev/null
+++ b/ext/openssl/tests/gh25172.phpt
@@ -0,0 +1,49 @@
+--TEST--
+GH-25172: TLS session reuse must not share callbacks with a persistent stream
+--EXTENSIONS--
+openssl
+--CREDITS--
+cxxz16
+--FILE--
+<?php
+function reuse_on_persistent(array $options, int $method): void {
+ $server = stream_socket_server('tcp://127.0.0.1:0');
+ $addr = stream_socket_get_name($server, false);
+ [$host, $port] = explode(':', $addr);
+
+ $ctx = stream_context_create(['ssl' => $options]);
+ $parent = stream_socket_client("tcp://$addr", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
+ stream_set_blocking($parent, false);
+ var_dump(stream_socket_enable_crypto($parent, true, $method));
+
+ $child = pfsockopen($host, (int) $port);
+ stream_set_blocking($child, false);
+ var_dump(stream_socket_enable_crypto($child, true, $method, $parent));
+}
+
+reuse_on_persistent(
+ ['psk_client_cb' => fn() => new Openssl\Psk(str_repeat('k', 32), 'id')],
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
+);
+reuse_on_persistent(
+ ['session_new_cb' => function () {}],
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
+);
+reuse_on_persistent(
+ ['early_data_cb' => function () {}],
+ STREAM_CRYPTO_METHOD_TLS_SERVER,
+);
+?>
+--EXPECTF--
+int(0)
+
+Warning: stream_socket_enable_crypto(): Supplied SSL session stream with callbacks is not supported for persistent streams in %s on line %d
+int(0)
+int(0)
+
+Warning: stream_socket_enable_crypto(): Supplied SSL session stream with callbacks is not supported for persistent streams in %s on line %d
+int(0)
+int(0)
+
+Warning: stream_socket_enable_crypto(): Supplied SSL session stream with callbacks is not supported for persistent streams in %s on line %d
+int(0)
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index f96c2cdb220..dcde68176b8 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -2797,6 +2797,14 @@ static zend_result php_openssl_setup_crypto(php_stream *stream,
php_stream_warn(stream, SslNotSupported, "Supplied SSL session stream is not set up");
} else if (sslsock->is_client && parent_sslsock->ssl_handle == NULL) {
php_stream_warn(stream, SslNotSupported, "Supplied SSL session stream is not initialized");
+ } else if (php_stream_is_persistent(stream) &&
+ (parent_sslsock->session_callbacks
+ || parent_sslsock->psk_callbacks
+#if defined(HAVE_TLS13)
+ || parent_sslsock->early_data_callbacks
+#endif
+ )) {
+ php_stream_warn(stream, PersistentNotSupported, "Supplied SSL session stream with callbacks is not supported for persistent streams");
} else {
SSL_CTX_up_ref(parent_sslsock->ctx);
sslsock->ctx = parent_sslsock->ctx;