-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid triggering SIGPIPE after stream_socket_shutdown(SHUT_WR) of a SSL stream #2605
Conversation
case STREAM_XPORT_OP_SHUTDOWN: | ||
if (sslsock->ssl_active && (xparam->how == STREAM_SHUT_WR || xparam->how == STREAM_SHUT_RDWR)) { | ||
if (SSL_shutdown(sslsock->ssl_handle) == -1) { | ||
php_stream_socket_ops.set_option(stream, option, value, ptrparam); /* ensure socket shutdown either way, but report failure */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually going to work for non-blocking socket? The thing is that if it is -1 then in case of the non-blocking socket the operation is not completed and it will have to be finished once the data can be read or written. It basically depends on SSL_get_error
result. So closing socket might not be a good idea maybe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we should just add a new stream_socket_shutdown_crypto
function?
I think there should be definitely a test for non-block with full buffer. The base could be similar like the one that I added for non-block write issue: https://github.com/php/php-src/blob/f066f59eab719d0171febf7bcd7f70edb5ffd6bd/ext/openssl/tests/bug72333.phpt . It would be also cool to have a test for blocking socket as well which should be even easier. ;) |
About the non-blocking I'm not sure how it should work from the stream point of view. It should definitely fail and not close socket immediately. Maybe something like error first and then allow using select so user can finish the closing when ready. It would probably need some extra logic though. |
Is it possible to add a test for this ? |
TL;DR for a blocking socket, it would be fine to do the In non-blocking mode, Really in order to do this for a non-blocking socket the logic needs to go like this (pseudo): while (!$result = stream_socket_enable_crypto($sock, false)) {
if ($result === false) {
goto failure;
}
poll_socket_for_read_or_write($sock);
}
stream_socket_shutdown($sock, STREAM_SHUT_WR);
failure:
fclose($sock); There are still a couple of problems with this in terms of the existing implementation, though.
I cannot come up with a way to truly resolve (1) without BC breaks, or introducing a new function. |
A possible solution that does not break BC would be to add a third optional by-ref out param to In order to make this work internally I see two options, equally distasteful in different ways:
I'm happy to work on a patch for this, though, as I think it would be worth doing, and that particular element of it is a relatively small part. |
After looking into this further, it seems that any I/O operation can potentially result in openssl_get_last_error(resource $stream): int This will allow userland to access this information when one of the following occurs for a non-blocking stream: fwrite($stream, $data) < strlen($data)
($data = fread($stream, $len)) !== false && strlen($data) < $len
stream_socket_enable_crypto($stream, ...) === 0
// other situations as well?
This would be implemented internally by proxying all calls to While exploring |
Both of these are quite common for non-blocking reads / writes. Does an app really need to check the error in these cases or can they be trimmed down? |
The way I read it, in these cases applications would need to check the error code. Consider the following situation in terms of how concurrency frameworks currently handle it:
In the opposite situation, where One thing that is notable about the There's no way (that I can see) to mitigate either of these without directly exposing This change would also solve the original problem that this PR was aiming for, in that it would enable a graceful SSL shutdown before shutting down the TCP layer. |
An empty read is only considered as a stream end if the resource is no longer valid or So the conditions would actually be What would an application actually call if it gets |
When you get |
To summarize the socket shutdown case: We'd call Am I understanding that correctly? If yes, that sounds like a nice API solution to me, given that there's no real break either. |
@bwoebi No, we'd not use |
uh, yes, confused the functions. You're right ;-) |
@bwoebi yes that's correct |
@DaveRandom I think it definitely makes sense to somehow expose the result from I think it's a good idea to separate just Also I think we should resolve the issue for |
@bukka agree on the naming completely, I was thinking the same to myself this morning. I was pondering suggesting renaming (with alias for BC) I've not really looked at the flush/close implementations. Intuitively I feel like flush should be different for a non-blocking stream, because if buffer size > send window then logically it could be a blocking call and/or return without completing in non-blocking mode, I will look into it further though. When a socket is in blocking mode, it absolutely does make sense to gracefully shut down SSL before shutting down the socket. In non-blocking mode I'm not sure it does because again, it may or may not be a blocking call for any given socket depending on the state of the connection and what's in the send buffer... my personal view is that when you put a socket into non-blocking mode you are essentially saying "I am taking responsibility for ensuring everything happens in the right order" and as such if you attempt to shutdown the socket without shutting down SSL it should maybe be a best-effort approach, that might result in an unclean shutdown of the SSL layer or perhaps will fail entirely - I'm very much open to persuasion on this point, though. What I'm bothered about is ensuring it is possible to do it "correctly" in non-blocking mode. I guess I might be OK with forcing the socket into blocking mode for the shutdown operation, since in the case of a socket where SSL has been properly shut down by the application it would still be an atomic operation, and if you didn't do that then it's "your fault" that it's now a blocking operation. I am planning to try to work up a patch this week, but I am also happy if someone else who can do it faster than me wants to take it on. I don't have loads of spare time and I will have to rtfm a bit to remind myself of how some stuff works. |
@DaveRandom I wouldn't probably rename I'm not sure what we should do in terms of flush actually? Currently it just flushes the underlying socket. Not too sure about close either. I guess it might need maybe some work to make sure that it is safe to close it for non-blocking but not sure atm. In terms of shutdown I think it would make sense just to return false if SSL_shutdown returns negative value for non-blocking and let user handle it in the same way as I won't have time to write a patch as I have bunch of other things on my list so it would be great if you find a time. I should be able to help with testing and review possibly. |
There's been no movement here in more than two years. It seems as if the discussion raised the possibility of another (API based) solution, which may be superior anyway. I'm closing this, if there is to be work on this issue please reference this PR from the new pull request (or RFC). |
Let $sock be a SSL stream, then
shall not trigger a SIGPIPE.
SSL_shutdown() is called from within fclose() currently and will try to write to the socket, thus that function must be called before SHUT_WR.
I am not sure about the correct behavior within non-blocking I/O streams when the buffer is full. … What will happen then? Will it try to dispatch the SSL alert a second time inside the SSL_shutdown() within fclose() then?
Do I need to set
sslsock->ssl_active = 0;
here? (but won't that then break the read pipe?!)please review - ping @bukka @DaveRandom