Skip to content

Commit e9873d9

Browse files
committed
Fix bug #74159
Thanks to @brzuchal for the patch to xp_ssl.c and @DaveRandom for helping debug the problem.
1 parent 330a7b6 commit e9873d9

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

ext/openssl/tests/bug74159.phpt

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
--TEST--
2+
Bug #74159: Writing a large buffer to non-blocking encrypted streams fails
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl")) die("skip openssl not loaded");
6+
if (!function_exists("proc_open")) die("skip no proc_open");
7+
--FILE--
8+
<?php
9+
$serverCode = <<<'CODE'
10+
$serverUri = "ssl://127.0.0.1:64321";
11+
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
12+
$serverCtx = stream_context_create(['ssl' => [
13+
'local_cert' => __DIR__ . '/bug54992.pem',
14+
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
15+
]]);
16+
17+
$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
18+
phpt_notify();
19+
20+
$client = stream_socket_accept($server, 1);
21+
22+
if (!$client) {
23+
exit();
24+
}
25+
26+
$data = '';
27+
while (strlen($data) < 0xfffff) {
28+
$buffer = fread($client, 8192);
29+
if (empty($buffer)) {
30+
exit();
31+
}
32+
$data .= $buffer;
33+
usleep(100);
34+
}
35+
36+
fclose($client);
37+
CODE;
38+
39+
$clientCode = <<<'CODE'
40+
function streamRead($stream) : int {
41+
return strlen(fread($stream, 8192));
42+
}
43+
44+
function streamWrite($stream, $data) : int {
45+
return fwrite($stream, $data);
46+
}
47+
48+
function waitForWrite(...$streams) : bool {
49+
$read = null;
50+
$except = null;
51+
while($streams && !($n = stream_select($read, $streams, $except, 1)));
52+
return $n > 0;
53+
}
54+
55+
function waitForRead(...$streams) : bool {
56+
$write = null;
57+
$except = null;
58+
while ($streams && !($n = stream_select($streams, $write, $except, 1)));
59+
return $n > 0;
60+
}
61+
62+
set_error_handler(function ($errno, $errstr) {
63+
exit("$errstr\n");
64+
});
65+
66+
$serverUri = "tcp://127.0.0.1:64321";
67+
$clientFlags = STREAM_CLIENT_CONNECT;
68+
$clientCtx = stream_context_create(['ssl' => [
69+
'verify_peer' => true,
70+
'cafile' => __DIR__ . '/bug54992-ca.pem',
71+
'peer_name' => 'bug54992.local',
72+
]]);
73+
74+
phpt_wait();
75+
76+
$fp = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
77+
78+
stream_set_blocking($fp, false);
79+
while (0 === ($n = stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_ANY_CLIENT)));
80+
81+
$data = str_repeat("a", 0xfffff);
82+
$written = 0;
83+
$total = $written;
84+
while(!empty($data)) {
85+
$written = streamWrite($fp, $data);
86+
$total += $written;
87+
$data = substr($data, $written);
88+
waitForWrite($fp);
89+
}
90+
printf("Written %d bytes\n", $total);
91+
92+
while(waitForRead($fp)) {
93+
streamRead($fp);
94+
if (feof($fp)) {
95+
break;
96+
}
97+
}
98+
99+
exit("DONE\n");
100+
CODE;
101+
102+
include 'ServerClientTestCase.inc';
103+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
104+
--EXPECTF--
105+
Written 1048575 bytes
106+
DONE

ext/openssl/xp_ssl.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,16 @@ int php_openssl_setup_crypto(php_stream *stream,
16661666
SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_RELEASE_BUFFERS);
16671667
} while (0);
16681668
#endif
1669+
1670+
do {
1671+
long mode = SSL_get_mode(sslsock->ssl_handle);
1672+
SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_ENABLE_PARTIAL_WRITE);
1673+
} while (0);
1674+
1675+
do {
1676+
long mode = SSL_get_mode(sslsock->ssl_handle);
1677+
SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
1678+
} while (0);
16691679

16701680
if (cparam->inputs.session) {
16711681
if (cparam->inputs.session->ops != &php_openssl_socket_ops) {

0 commit comments

Comments
 (0)