Skip to content

Commit 3571d36

Browse files
committed
Merge branch 'PHP-8.6'
* PHP-8.6: Fix TLS stream EOF detection after close_notify with stale errno (#24132)
2 parents 421347d + e91a843 commit 3571d36

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
feof() is true after a TLS close_notify even if an earlier read left errno set to EAGAIN
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
?>
9+
--FILE--
10+
<?php
11+
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_eof_after_close_notify.pem.tmp';
12+
13+
$serverCode = <<<'CODE'
14+
$serverCtx = stream_context_create(['ssl' => ['local_cert' => '%s']]);
15+
$sock = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr,
16+
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
17+
phpt_notify_server_start($sock);
18+
19+
$link = stream_socket_accept($sock);
20+
/* Let the client block in fread() first, so its SSL_read() sees WANT_READ */
21+
phpt_wait();
22+
usleep(100000);
23+
fwrite($link, "data");
24+
/* close_notify only, the TCP connection stays open */
25+
stream_socket_enable_crypto($link, false);
26+
phpt_wait();
27+
fclose($link);
28+
CODE;
29+
$serverCode = sprintf($serverCode, $certFile);
30+
31+
$clientCode = <<<'CODE'
32+
$clientCtx = stream_context_create(['ssl' => [
33+
'verify_peer' => false,
34+
'verify_peer_name' => false,
35+
]]);
36+
$sock = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 2, STREAM_CLIENT_CONNECT, $clientCtx);
37+
38+
phpt_notify();
39+
var_dump(fread($sock, 4));
40+
var_dump(fread($sock, 4));
41+
var_dump(feof($sock));
42+
phpt_notify();
43+
CODE;
44+
45+
include 'CertificateGenerator.inc';
46+
(new CertificateGenerator())->saveNewCertAsFileWithKey('stream_eof_after_close_notify', $certFile);
47+
48+
include 'ServerClientTestCase.inc';
49+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
50+
?>
51+
--CLEAN--
52+
<?php
53+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'stream_eof_after_close_notify.pem.tmp');
54+
?>
55+
--EXPECT--
56+
string(4) "data"
57+
string(0) ""
58+
bool(true)

‎ext/openssl/xp_ssl.c‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,9 +3243,11 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si
32433243
retry = 1;
32443244
}
32453245

3246-
/* Also, on reads, we may get this condition on an EOF. We should check properly. */
32473246
if (read) {
3248-
stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
3247+
/* EOF unless the SSL layer just needs to wait. */
3248+
stream->eof = (retry == 0
3249+
&& err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE
3250+
&& !SSL_pending(sslsock->ssl_handle));
32493251
}
32503252

32513253
/* Don't loop indefinitely in non-blocking mode if no data is available */

0 commit comments

Comments
 (0)