php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75288 stream_socket_enable_crypto() blocks during handshake
Submitted: 2017-09-29 19:10 UTC Modified: -
Votes:3
Avg. Score:4.0 ± 1.4
Reproduced:3 of 3 (100.0%)
Same Version:1 (33.3%)
Same OS:1 (33.3%)
From: james at jamesreno dot com Assigned:
Status: Open Package: Streams related
PHP Version: 7.0.24 OS: Ubuntu 16.04
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2017-09-29 19:10 UTC] james at jamesreno dot com
Description:
------------
When enabling ssl on a socket with php 7.0.24 the handshake blocks the socket. I expect the enable_crypto to immediately return "0" if the socket is in non-blocking mode and there is not enough data available to perform the handshake.

Test script:
---------------
<?php
$ssl_stream_ctx = stream_context_create(array(
	'ssl' => array(
		'local_cert'	=> 'cert.pem',
		'verify_peer'	=> FALSE,
	),
));

$listener = @stream_socket_server("tcp://127.0.0.1:1234",$errno,$errstr,STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ssl_stream_ctx);
stream_set_blocking($listener, FALSE);
$peerName = FALSE;
$sock = @stream_socket_accept($fd, 0.01, $peerName);
stream_set_blocking($sock, FALSE);
$data = stream_socket_recvfrom($sock,1,STREAM_PEEK);
if ($data[0] == "\x16") {
	$sslInitStartTime = microtime(TRUE);
	$ret = stream_socket_enable_crypto($sock, TRUE, STREAM_CRYPTO_METHOD_SSLv23_SERVER);
	$sslInitEndTime = microtime(TRUE);
	$sslInitTime = $sslInitEndTime - $sslInitStartTime;
				
	if ($sslInitTime >= 0.1) {
echo "ERROR SSL INIT TOOK TOO LONG." . PHP_EOL;
	}


}

Expected result:
----------------
Expect to return immediately and not hange > 0.1sec

Actual result:
--------------
Script hangs sometimes up to several seconds with "far away high-latency" clients.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-09-29 19:15 UTC] james at jamesreno dot com
Might be related to the fix for one of the following bugs:
- bug#72333: https://bugs.php.net/bug.php?id=72333 fwrite() on non-blocking SSL sockets doesn't work
- bug#45808: https://bugs.php.net/bug.php?id=72333 stream_socket_enable_crypto() blocks during handshake
 [2017-09-29 21:38 UTC] james at jamesreno dot com
in ext/openssl/xp_ssl.c there is a line:

has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);

From what I can gather this means that if a socket timeout is specified in the php.ini/config and then non-blocking is enabled the socket will WAIT upto a maximum of the default time and will hang a non-blocking socket due to the if (has_timeout) {} checking.

                                        if (has_timeout) {
                                                left_time = subtract_timeval( *timeout, elapsed_time );
                                        }
                                        php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
                                                (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);

This is passed through to php_pollfd_for() instead of NULL which causes the non-blocking socket to "block".  I believe if blocking is set, this part should ALWAYS be passed NULL so

the line that reads:

                                        php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
                                                (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);


Should be changed to:

                                        php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
                                                (POLLIN|POLLPRI) : POLLOUT, !blocked && has_timeout ? &left_time : NULL);
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Jun 16 21:01:28 2019 UTC