Commit 97ce0bf86bf for php
commit 97ce0bf86bfcbebb865f15f594d2f05da908dccd
Author: Daniel Scherzer <daniel.e.scherzer+phpf@gmail.com>
Date: Thu Oct 1 19:49:07 2026 -0700
ext/openssl: fix use after free when `psk_server_cb` callback closes stream
diff --git a/ext/openssl/tests/close_in_psk_server_cb_UAF.phpt b/ext/openssl/tests/close_in_psk_server_cb_UAF.phpt
new file mode 100644
index 00000000000..3a561ef877e
--- /dev/null
+++ b/ext/openssl/tests/close_in_psk_server_cb_UAF.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Closing the stream in the `psk_server_cb` callback triggers use after free
+--EXTENSIONS--
+openssl
+--CREDITS--
+Calif.io
+--FILE--
+<?php
+
+$serverCode = <<<'PHP'
+$context = stream_context_create(['ssl' => [
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER,
+ 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk {
+ fclose($stream);
+ return null;
+ },
+]]);
+
+$server = stream_socket_server(
+ 'tls://127.0.0.1:0',
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ $context,
+);
+phpt_notify_server_start($server);
+@stream_socket_accept($server, 10);
+PHP;
+
+$clientCode = <<<'PHP'
+$context = stream_context_create(['ssl' => [
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ 'psk_client_cb' => fn ($stream) => new Openssl\Psk(
+ str_repeat("\x42", 32),
+ 'remote-identity',
+ ),
+]]);
+
+@stream_socket_client(
+ 'tls://{{ ADDR }}',
+ $errno,
+ $errstr,
+ 10,
+ STREAM_CLIENT_CONNECT,
+ $context,
+);
+PHP;
+
+require 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
+
+?>
+--EXPECT--
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 56ae65171b1..c67517b06c7 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -1997,11 +1997,19 @@ static int php_openssl_psk_find_session_cb(SSL *ssl, const unsigned char *identi
return 1;
}
+ bool originally_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
+ stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
zval result;
if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->server_cb,
identity, identity_len, &result) != SUCCESS) {
+ if (!originally_no_fclose) {
+ stream->flags ^= PHP_STREAM_FLAG_NO_FCLOSE;
+ }
return 0;
}
+ if (!originally_no_fclose) {
+ stream->flags ^= PHP_STREAM_FLAG_NO_FCLOSE;
+ }
if (Z_TYPE(result) == IS_NULL) {
return 1; /* identity unknown - let handshake fall through */