Commit 24ca066b464 for php
commit 24ca066b4649eea0b9a8abfa574fad67b834cca2
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Thu Nov 5 09:49:11 2026 -0500
ext/openssl: Propagate session_get_cb exceptions unchanged
An exception thrown by session_get_cb left the call's return value
undefined, which the return type check then reported as a TypeError
chained over the user's exception. Skip that check for an undefined
return value, as the PSK callbacks already do, so the original exception
reaches the caller.
Closes GH-25218
diff --git a/NEWS b/NEWS
index af1e692a180..a25df1bb1ca 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,8 @@ PHP NEWS
- OpenSSL:
. Fixed bug GH-25172 (TLS session reuse shares request-lifetime callback
structures with persistent streams). (cxxz16, David Carlier)
+ . Fixed an exception thrown by the session_get_cb stream context callback
+ being replaced with a TypeError. (Ilia Alshanetsky)
05 Nov 2026, PHP 8.6.0RC5
diff --git a/ext/openssl/tests/session_resumption_get_cb_throws.phpt b/ext/openssl/tests/session_resumption_get_cb_throws.phpt
new file mode 100644
index 00000000000..1d6b461a2ef
--- /dev/null
+++ b/ext/openssl/tests/session_resumption_get_cb_throws.phpt
@@ -0,0 +1,98 @@
+--TEST--
+TLS session resumption - exception thrown by session_get_cb propagates unchanged
+--EXTENSIONS--
+openssl
+--SKIPIF--
+<?php
+if (!function_exists("proc_open")) die("skip no proc_open");
+?>
+--FILE--
+<?php
+$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_get_cb_throws.pem.tmp';
+
+$serverCode = <<<'CODE'
+ $sessionStore = [];
+
+ $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+ $ctx = stream_context_create(['ssl' => [
+ 'local_cert' => '%s',
+ 'session_id_context' => 'test-server',
+ 'session_new_cb' => function ($stream, $session) use (&$sessionStore) {
+ $sessionStore[bin2hex($session->id)] = $session;
+ },
+ 'session_get_cb' => function ($stream, $sessionId) use (&$sessionStore) {
+ if (isset($sessionStore[bin2hex($sessionId)])) {
+ throw new RuntimeException('session_get_cb failed');
+ }
+ return null;
+ },
+ ], 'stream' => ['error_mode' => StreamErrorMode::%s]]);
+
+ $server = stream_socket_server('tlsv1.2://127.0.0.1:0', $errno, $errstr, $flags, $ctx);
+ phpt_notify_server_start($server);
+
+ $client = @stream_socket_accept($server, 30);
+ fwrite($client, "Response 1\n");
+ fclose($client);
+
+ try {
+ $client = @stream_socket_accept($server, 30);
+ $result = "accepted " . get_debug_type($client);
+ } catch (Throwable $e) {
+ $result = $e::class . ": " . $e->getMessage() . ", previous: " . get_debug_type($e->getPrevious());
+ }
+
+ phpt_notify(message: $result);
+CODE;
+
+$clientCode = <<<'CODE'
+ $sessionData = null;
+
+ $flags = STREAM_CLIENT_CONNECT;
+ $ctx = stream_context_create(['ssl' => [
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ 'session_new_cb' => function ($stream, $session) use (&$sessionData) {
+ $sessionData = $session;
+ },
+ ]]);
+
+ $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx);
+ echo trim(fgets($client1)), "\n";
+ fclose($client1);
+
+ $ctx2 = stream_context_create(['ssl' => [
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ 'session_data' => $sessionData,
+ ]]);
+
+ $client2 = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2);
+ var_dump($client2);
+
+ echo "Server: ", trim(phpt_wait()), "\n";
+CODE;
+
+include 'CertificateGenerator.inc';
+$certificateGenerator = new CertificateGenerator();
+$certificateGenerator->saveNewCertAsFileWithKey('session_get_cb_throws', $certFile);
+
+include 'ServerClientTestCase.inc';
+foreach (['Error', 'Exception'] as $mode) {
+ echo "$mode mode:\n";
+ ServerClientTestCase::getInstance()->run($clientCode, sprintf($serverCode, $certFile, $mode));
+}
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'session_get_cb_throws.pem.tmp');
+?>
+--EXPECTF--
+Error mode:
+Response 1
+resource(%d) of type (stream)
+Server: RuntimeException: session_get_cb failed, previous: null
+Exception mode:
+Response 1
+resource(%d) of type (stream)
+Server: RuntimeException: session_get_cb failed, previous: null
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index dcde68176b8..f4f6a932efe 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -2266,7 +2266,7 @@ static SSL_SESSION *php_openssl_session_get_cb(SSL *ssl, const unsigned char *se
SSL_SESSION_up_ref(obj->session);
session = obj->session;
}
- } else if (Z_TYPE(retval) != IS_NULL) {
+ } else if (!Z_ISUNDEF(retval) && Z_TYPE(retval) != IS_NULL) {
zend_type_error("session_get_cb return type must be null or Openssl\\Session");
}