diff options
author | Aaron Piotrowski <aaron@trowski.com> | 2017-02-23 20:58:12 -0600 |
---|---|---|
committer | Aaron Piotrowski <aaron@trowski.com> | 2017-02-23 20:59:43 -0600 |
commit | e9873d9853da2b6153776c2e5ed6858e87f0baa2 (patch) | |
tree | 459cb038968167aa5237248e9ab6e38aa853c211 | |
parent | 330a7b62c3558aa987ee80e12f1914347d3a9eee (diff) | |
download | php-git-e9873d9853da2b6153776c2e5ed6858e87f0baa2.tar.gz |
Fix bug #74159
Thanks to @brzuchal for the patch to xp_ssl.c and @DaveRandom for helping debug the problem.
-rw-r--r-- | ext/openssl/tests/bug74159.phpt | 106 | ||||
-rw-r--r-- | ext/openssl/xp_ssl.c | 10 |
2 files changed, 116 insertions, 0 deletions
diff --git a/ext/openssl/tests/bug74159.phpt b/ext/openssl/tests/bug74159.phpt new file mode 100644 index 0000000000..0299cf117a --- /dev/null +++ b/ext/openssl/tests/bug74159.phpt @@ -0,0 +1,106 @@ +--TEST-- +Bug #74159: Writing a large buffer to non-blocking encrypted streams fails +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip openssl not loaded"); +if (!function_exists("proc_open")) die("skip no proc_open"); +--FILE-- +<?php +$serverCode = <<<'CODE' + $serverUri = "ssl://127.0.0.1:64321"; + $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $serverCtx = stream_context_create(['ssl' => [ + 'local_cert' => __DIR__ . '/bug54992.pem', + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + ]]); + + $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); + phpt_notify(); + + $client = stream_socket_accept($server, 1); + + if (!$client) { + exit(); + } + + $data = ''; + while (strlen($data) < 0xfffff) { + $buffer = fread($client, 8192); + if (empty($buffer)) { + exit(); + } + $data .= $buffer; + usleep(100); + } + + fclose($client); +CODE; + +$clientCode = <<<'CODE' + function streamRead($stream) : int { + return strlen(fread($stream, 8192)); + } + + function streamWrite($stream, $data) : int { + return fwrite($stream, $data); + } + + function waitForWrite(...$streams) : bool { + $read = null; + $except = null; + while($streams && !($n = stream_select($read, $streams, $except, 1))); + return $n > 0; + } + + function waitForRead(...$streams) : bool { + $write = null; + $except = null; + while ($streams && !($n = stream_select($streams, $write, $except, 1))); + return $n > 0; + } + + set_error_handler(function ($errno, $errstr) { + exit("$errstr\n"); + }); + + $serverUri = "tcp://127.0.0.1:64321"; + $clientFlags = STREAM_CLIENT_CONNECT; + $clientCtx = stream_context_create(['ssl' => [ + 'verify_peer' => true, + 'cafile' => __DIR__ . '/bug54992-ca.pem', + 'peer_name' => 'bug54992.local', + ]]); + + phpt_wait(); + + $fp = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); + + stream_set_blocking($fp, false); + while (0 === ($n = stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_ANY_CLIENT))); + + $data = str_repeat("a", 0xfffff); + $written = 0; + $total = $written; + while(!empty($data)) { + $written = streamWrite($fp, $data); + $total += $written; + $data = substr($data, $written); + waitForWrite($fp); + } + printf("Written %d bytes\n", $total); + + while(waitForRead($fp)) { + streamRead($fp); + if (feof($fp)) { + break; + } + } + + exit("DONE\n"); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +--EXPECTF-- +Written 1048575 bytes +DONE diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index b4387cd738..9dd402a8bd 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -1666,6 +1666,16 @@ int php_openssl_setup_crypto(php_stream *stream, SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_RELEASE_BUFFERS); } while (0); #endif + + do { + long mode = SSL_get_mode(sslsock->ssl_handle); + SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_ENABLE_PARTIAL_WRITE); + } while (0); + + do { + long mode = SSL_get_mode(sslsock->ssl_handle); + SSL_set_mode(sslsock->ssl_handle, mode | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + } while (0); if (cparam->inputs.session) { if (cparam->inputs.session->ops != &php_openssl_socket_ops) { |