Repository navigation
feof hangs indefinitely #10495
Copy link
Copy link
Closed
Description
Description
I am using horde/imap-client to connect to an IMAPS server. If I disconnect the local network interface after a couple of iterations of https://github.com/bytestream/Imap_Client/blob/ac98de92168777fb8bf0bacf3129fc178523defe/lib/Horde/Imap/Client/Socket/Connection/Socket.php#L186 when it's downloading a 30MB email body then feof hangs indefintely. stream_set_timeout(30); and default_socket_timeout of 60 seem to have no effect...
The following is an example to illustrate the problem, and should not be used as a test case:
<?php
$_stream = @stream_socket_client(
"ssl://server.com:993",
$error_number,
$error_string,
30,
STREAM_CLIENT_CONNECT,
stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'allow_self_signed' => true,
],
])
);
stream_set_timeout($_stream, 30);
stream_set_read_buffer($_stream, 0);
stream_set_write_buffer($_stream, 0);
while (!feof($_stream)) {
$in = fread($_stream, 8192);
}If I dump stream metadata, the result never changes:
array(8) {
["crypto"]=>
array(4) {
["protocol"]=>
string(7) "TLSv1.2"
["cipher_name"]=>
string(27) "ECDHE-RSA-AES128-GCM-SHA256"
["cipher_bits"]=>
int(128)
["cipher_version"]=>
string(7) "TLSv1.2"
}
["timed_out"]=>
bool(false)
["blocked"]=>
bool(true)
["eof"]=>
bool(false)
["stream_type"]=>
string(14) "tcp_socket/ssl"
["mode"]=>
string(2) "r+"
["unread_bytes"]=>
int(0)
["seekable"]=>
bool(false)
}
When using slightly modified code of:
while ($literal_len > 0) {
sleep(3);
if (! is_resource($this->_stream) || feof($this->_stream)) {
strace at the point where the process hangs shows:
16:57:11.921521 clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0}, 0x7fffb50a22b0) = 0
16:57:14.930968 poll([{fd=11, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=11, revents=POLLIN}])
16:57:14.931033 read(11, "\27\3\3\20\30", 5) = 5
| 00000 17 03 03 10 18 ..... |
16:57:14.931068 read(11, "\26c\306", 4120) = 3
| 00000 16 63 c6 .c. |
16:57:14.931094 read(11,
File descriptor 11 is the socket to the IMAPS server.
PHP Version
PHP 8.1.11
Operating System
No response
Activity
bukka commentedon May 6, 2023
I just tried to recreate this but not successful so might need more details and ideally server code that can be used for recreating. I tried with this:
Server:
Client:
And tried to use this testing cert:
I tried different option with terminating the server but no luck. If you could modify it in some reliable way or provide exact steps how to recreate it, that would be great!
github-actions commentedon May 21, 2023
No feedback was provided. The issue is being suspended because we assume that you are no longer experiencing the problem. If this is not the case and you are able to provide the information that was requested earlier, please do so. Thank you.
driskell commentedon May 21, 2023
Just wanted to note that I experienced this symptom with TLS 1.3 and eventually came across https://bugs.php.net/bug.php?id=79501
Workaround for me was to force 1.2 but of course not a permanent solution but noting here in case people come across this thread before the above one.
bytestream commentedon May 21, 2023
Thanks @bukka for the client and server to help reproduce - that's very helpful.
Thanks @driskell for linking that issue, it does sound like what I've described... and @bukka latest solution on the issue is pretty much what I ended up implementing to work around it.
bukka commentedon May 21, 2023
@bytestream Just to clarify I have not been able to reproduce it with the script above and that's why I was asking about more feedback. In terms of solution, are you talking about using non blocking socket instead? If so it's just a workaround as non blocking is using different paths. It would be still appreciated to be able to recreate for blocking socket. Are you able to reliably recreate the problem (even with all dependencies - does not need to be necessarily extracted script if it's too hard to craete)? Does it happen with horde client. And what's the imap server?
From what I see in metadata in this bug report above, then it says TLSv1.2 protocol so seems like potentially somethings else. Although that issue can happen for 1.2 but it is a less likely to be visible (it might happen during renegotiation for example).
bytestream commentedon May 22, 2023
Steps to replicate:
git clone https://github.com/bytestream/php-src-10495cd php-src-10495/test.phpand configure username[email protected], password, and hostpsec ofmail.php.net[email protected]containingrandom.imgas an attachment.Make sure that it's the only unread email in your
INBOX... the script will download and mark all unread emails as read...test.phpand disconnect the network (kill the network manager, pull the ethernet, disconnect the wifi) when it'sDownloading emails...The output will stop and it just hangs forever.
bytestream/Imap_Client#10 is the workaround I added to temporarily use a non blocking socket.
bukka commentedon Jun 9, 2023
@bytestream Thanks for the replication steps. I just tried it.
Firstly php.net emails are just aliases and the one that I use just points to gmail. I have just gmail accounts actually so tried to set it up with gmail but unfortunately it fails on
I use the same creds as for login as documented in https://support.google.com/a/answer/9003945?hl=en#zippy=%2Cstep-turn-on-imap-in-gmail and IMAP is of course enable so no idea why it doesn't work. I spent a bit of time trying to find a solution but no luck so far - I'm not really an expert on IMAP tbh and don't want to waste that much my dev time on this. Are you able to make it work with any gmail account or potentially another provider where I can get free inbox?
bytestream commentedon Jun 9, 2023
@bukka I think you would need to use either an app password, or oauth for Gmail / Microsoft. They recently started enforcing oauth which is a bit more complex to setup.
I could give you a mailbox on one of my servers? Then at least we're on the same page from a replication perspective. Let me know if you're ok with that and I'll email you the details.
bukka commentedon Jun 9, 2023
@bytestream That would be great! Please email me the details and I will give it a try.
bukka commentedon Dec 21, 2023
So I have finally done some debugging of this and it is caused pretty much the same part of code as https://bugs.php.net/bug.php?id=79501 (specifically it happens on SSL_peek that can block forever). I think we will need to make this check non blocking as poll is not enough here. The problem is that if the whole record is not read, it will block which is what I think is happening here. The Redis issue is slightly different as it might also block on write but effectively the same fix should fix both issues.
I have started looking to a standard test for this which is a bit tricky and having some issues with getting proxy layer working in the way I want so will need to do some debugging there. I would like to also produce test for write part (key update) so might need something more sophisticated potentially. I will be looking more into it in the coming months - it's my top TLS priority so should hopefully get fixed at some point.
bukka commentedon Feb 24, 2024
PR for the fix create: #13487
Fix phpGH-10495: feof on OpenSSL stream hangs indefinitely
Fix GH-10495: feof on OpenSSL stream hangs indefinitely
Fix test for GH-10495: feof on OpenSSL stream hangs