php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73929 SSL client socket errors under load with SSL_CTX_new:null ssl method passed in
Submitted: 2017-01-14 00:19 UTC Modified: -
Votes:2
Avg. Score:3.5 ± 0.5
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:1 (50.0%)
From: tonyp at valvesoftware dot com Assigned:
Status: Open Package: Sockets related
PHP Version: 7.0.14 OS: Ubuntu 16.04
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: tonyp at valvesoftware dot com
New email:
PHP Version: OS:

 

 [2017-01-14 00:19 UTC] tonyp at valvesoftware dot com
Description:
------------
Almost every hit to our web server makes a socket request to another server. Under load, when using PHP 7, we get errors like the following:
[Fri Jan 13 15:27:28.692030 2017] [proxy_fcgi:error] [pid 2362] [client <redacted>:60929] AH01071: Got error 'PHP message: PHP Warning:  fread(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:140A90C4:SSL routines:SSL_CTX_new:null ssl method passed in <redacted>
[Fri Jan 13 15:27:28.751464 2017] [proxy_fcgi:error] [pid 1960] [client <redacted>:61262] AH01071: Got error 'PHP message: PHP Warning:  fread(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:140A90C4:SSL routines:SSL_CTX_new:null ssl method passed in <redacted>
[Fri Jan 13 15:27:29.404802 2017] [proxy_fcgi:error] [pid 2362] [client <redacted>:60929] AH01071: Got error 'PHP message: PHP Warning:  fread(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:140A90C4:SSL routines:SSL_CTX_new:null ssl method passed in <redacted>
[Fri Jan 13 15:27:29.487308 2017] [proxy_fcgi:error] [pid 2346] [client <redacted>:63548] AH01071: Got error 'PHP message: PHP Warning:  fread(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:140A90C4:SSL routines:SSL_CTX_new:null ssl method passed in <redacted>
[Fri Jan 13 15:27:29.668853 2017] [proxy_fcgi:error] [pid 2420] [client <redacted>:42796] AH01071: Got error 'PHP message: PHP Warning:  fread(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:140A90C4:SSL routines:SSL_CTX_new:null ssl method passed in <redacted>

I have verified that it's actually a socket resource being passed into fread.

We do not get these errors in PHP 5. We do not get errors when using non-SSL sockets. Using persistent sockets seems to make the errors occur more frequently, but the problem still occurs when not using persistent sockets. We do use non-SSL persistent sockets elsewhere for things like connections to memcache (Memcache::pconnect).

I have tried PHP as an Apache module, and I have tried PHP-FPM. The problem occurs in both.

Unfortunately, I have not been able to come up with a reliable repro test script. I have got the error to occur in a script that spins up 1,000 forks and has each fork connect and send data back and forth using SSL sockets, but it's very unreliable. I'm not sure what's happening in production to make the error happen more frequently.

The sockets are created using stream_socket_client with the STREAM_CLIENT_CONNECT flag. We create a context using stream_context_create and set allow_self_signed to true. verify_peer and verify_peer_name are set to false. I have not been able to verify whether or not the errors occur without passing or context, or whether or not they occur using fsockopen. The errors occur if even if a specific cipher is specified, or at least occur with the ciphers I've tried (ECDHE-RSA-AES256-GCM-SHA384 for example).

The errors occur using ssl://, tls://, and tlsv1.2:// transports. The errors still occur when adding a random identifier after the ip:port combo, for example: 'tls://10.1.2.3:1234/' . rand(0,999999).

The code that's failing is an fread call immediately after an fwrite. The fwrite call returns a positive value as it should, and the fread call fails immediately. I have not verified whether or not the data sent by the fwrite call preceding the failing fread call is actually received by the server.

Let me know if there's something I can try that will give you more information about the nature of the problem.

Test script:
---------------
$context = stream_context_create();
stream_context_set_option( $context, 'ssl', 'allow_self_signed', true );
stream_context_set_option( $context, 'ssl', 'verify_peer', false );
stream_context_set_option( $context, 'ssl', 'verify_peer_name', false );

$this->m_pfSocket = stream_socket_client( 'tlsv1.2://' . $ip . ':' . $port, $errno, $errstr, 1.0, STREAM_CLIENT_CONNECT, $context );



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-01-27 23:29 UTC] henryg at valvesoftware dot com
The bug is actually that the CURL extension doesn't always clear the OpenSSL error stack upon encountering an error, and then later the PHP socket implementation unconditionally checks SSL_get_error even when the return value from SSL_read or SSL_write is positive (which should indicate no error). PHP then believes that the error was related to the socket operation and aborts the operation, when really the error was unrelated and just happened to be sitting on the OpenSSL error stack.
 [2017-01-31 00:42 UTC] henryg at valvesoftware dot com
The PHP OpenSSL socket code is fragile in the face of any existing OpenSSL errors. The function which performs SSL_read and SSL_write incorrectly fail the socket operation even when the operations succeeded, but there was a pre-existing OpenSSL error.

Many extensions such as CURL can fill the OpenSSL error stack even when called from perfectly valid PHP code, depending only on the behavior of the remote server. The only way to reliably use fsockopen and other stream sockets with SSL in PHP is to always manually clear the OpenSSL error stack using openssl_error_string() in a loop before every call to fsockopen(), fread() or fwrite(). This is obviously not reasonable behavior; the PHP OpenSSL socket implementation should instead clear the OpenSSL error state (possibly reporting it to the log) before attempting new operations, but the new operations should not fail due to old errors.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu Jul 18 08:01:25 2019 UTC