[php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6
| From: | Jakub Zelenka | Date: | Mon, 05 Oct 2026 09:38:21 +0000 |
| Subject: | [php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141952@lists.php.net to get a copy of this message | ||
Author: Jakub Zelenka (bukka)
Date: 2026-10-05T11:38:10+02:00
Commit: https://github.com/php/php-src/commit/e91a8435995edb72661e0e9bf5c82c544c8889c6
Raw diff: https://github.com/php/php-src/commit/e91a8435995edb72661e0e9bf5c82c544c8889c6.diff
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
Fix TLS stream EOF detection after close_notify with stale errno (#24132)
Changed paths:
A ext/openssl/tests/stream_eof_after_close_notify.phpt
M NEWS
M ext/openssl/xp_ssl.c
Diff:
diff --git a/NEWS b/NEWS
index b94fc6ba52b1..bc63b84709c2 100644
--- a/NEWS
+++ b/NEWS
@@ -76,6 +76,8 @@ PHP NEWS
. Fix memory leak by doing early salt validation. (adapik)
. Fixed openssl_sign() using the maximum RSA-PSS salt length by default
instead of the OpenSSL default. (adapik)
+ . Fixed feof() on a TLS stream staying false after a close_notify on Windows.
+ (Jakub Zelenka)
- PCNTL:
. Fixed pcntl_signal_dispatch() dropping the queued signals when it runs while
diff --git a/ext/openssl/tests/stream_eof_after_close_notify.phpt
b/ext/openssl/tests/stream_eof_after_close_notify.phpt
new file mode 100644
index 000000000000..4755e171544d
--- /dev/null
+++ b/ext/openssl/tests/stream_eof_after_close_notify.phpt
@@ -0,0 +1,58 @@
+--TEST--
+feof() is true after a TLS close_notify even if an earlier read left errno set to EAGAIN
+--EXTENSIONS--
+openssl
+--SKIPIF--
+<?php
+if (!function_exists("proc_open")) die("skip no proc_open");
+?>
+--FILE--
+<?php
+$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_eof_after_close_notify.pem.tmp';
+
+$serverCode = <<<'CODE'
+ $serverCtx = stream_context_create(['ssl' => ['local_cert' =>
'%s']]);
+ $sock = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
+ phpt_notify_server_start($sock);
+
+ $link = stream_socket_accept($sock);
+ /* Let the client block in fread() first, so its SSL_read() sees WANT_READ */
+ phpt_wait();
+ usleep(100000);
+ fwrite($link, "data");
+ /* close_notify only, the TCP connection stays open */
+ stream_socket_enable_crypto($link, false);
+ phpt_wait();
+ fclose($link);
+CODE;
+$serverCode = sprintf($serverCode, $certFile);
+
+$clientCode = <<<'CODE'
+ $clientCtx = stream_context_create(['ssl' => [
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ ]]);
+ $sock = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 2,
STREAM_CLIENT_CONNECT, $clientCtx);
+
+ phpt_notify();
+ var_dump(fread($sock, 4));
+ var_dump(fread($sock, 4));
+ var_dump(feof($sock));
+ phpt_notify();
+CODE;
+
+include 'CertificateGenerator.inc';
+(new
CertificateGenerator())->saveNewCertAsFileWithKey('stream_eof_after_close_notify',
$certFile);
+
+include 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'stream_eof_after_close_notify.pem.tmp');
+?>
+--EXPECT--
+string(4) "data"
+string(0) ""
+bool(true)
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index cf3793fd5514..ad06af444ffe 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -3243,9 +3243,11 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf,
si
retry = 1;
}
- /* Also, on reads, we may get this condition on an EOF. We should check properly. */
if (read) {
- stream->eof = (retry == 0 && errno != EAGAIN &&
!SSL_pending(sslsock->ssl_handle));
+ /* EOF unless the SSL layer just needs to wait. */
+ stream->eof = (retry == 0
+ && err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE
+ && !SSL_pending(sslsock->ssl_handle));
}
/* Don't loop indefinitely in non-blocking mode if no data is available */