-
Notifications
You must be signed in to change notification settings - Fork 87
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
TLS streams are not guaranteed to be "split" (full-duplex) safe #40
Comments
I had another thought on how to work around this in a generic fashion:
That would lead to spurious wakeups on the opposite direction and 50% wasted syscalls, but the same also happens if someone performs concurrent reads/writes on a |
I believe that tokio-rustls will hardly have such problems. The only place it performs IO in the opposite direction is when it tries to send an alert when an error occurs. but the old version of tokio-rustls and some fork based on it do have this problem. like async-tls |
I agree this should be safe, since this is a terminal condition anyway. The downside of this model is that if rustls itself would require e.g. sending re-negotations or other control data on reads It might be more tricky to find out which ones of the I also looked at async-tls which you linked. That one indeed seems generally not safe to |
It sounds to me that the TLS streams have buggy implementation of the traits. Another way to fix this would be for the TlsStream to hold its own read/write wakers and when calling the inner stream, it passes a weaker that fans out notification to both the read / write half. This will result in spurious wakeups but will fix the implmentations. |
cc @sfackler |
This sounds dangerous and can render split unusable...Any progress on this? I happened to want to use |
Hi, would it be a good idea the library provide callback functions for TLS control data? |
dose still has this problem now? |
I think the issue will always be there because it depends on the implementations, which at the end, are an infinite amount of crates. |
Currently every TLS stream can be split into a reading half and a writing half using
tokio::util::split
. This operation is however not always guaranteed to be correct and safe to perform:The reason here is that with TLS reading and writing on a stream are not guaranteed to be decoupled, since TLS streams also transfer control data besides application data. Due to this, performing a read operation on the TLS stream might trigger a write operation on the socket (e.g. for sending a key update or alert), and the other way around.
This property can break assumptions that tokio/tokio-tls and the libraries make at the moment. As one example:
write
on the underlying socket, which report a blocked status. It forwards that blocked status to the application.write
readiness might be woken up. So the reading half would be stuck.Waker
for write readiness. If it instead identifies being write blocked, and overwrites thewrite
Waker
the situation is not necessarily better. Now a concurrent write operation might instead be starved, since theWaker
is gone.What will exactly happen or won't happen is a bit of a property of the TLS library, and might therefore be handled completely different by rustls, openssl, schannel and security-framework. Therefore it's rather hard to describe what exactly "could go wrong" if someone tries to split a TLS stream.
With
rustls
reading and writing onto actual sockets is currently purely handled bytokio-tls
, therefore its the most easy thing to grasp. Here any read or write operations in the wrapper simply don't seem to care whether rustls wants to perform IO in the opposite direction. That might not lead toWaker
stealing and tasks getting starved. However I guess it could lead to a delay in sending certain updates. @ctz might know more whether this is problematic.With
native-tls
+openssl
it is very likely that it will perform IO in the opposite direction and handle readiness notifications wrong. https://dzone.com/articles/using-openssl-with-libuv provides a bit of information what needs to be done to handle those cases, but I thinknative-tls
doesn't since it purely forwards all TLS calls to openssl which uses thefd
to perform IO. From there on things rely onmio
to report readiness, which only cares about the sockets read/write state and not the TLS streams read/write state.How can this be fixed
It's unfortunately not easy. To avoid people running into starved streams, one solution is to get rid of
tokio::util::split
and hightlight in docs that streams support only one commonWaker
for both read and write operations. But that's not making streams full duplex.I think to really enable full-duplex the following things could be done:
rustls
is already doing.This is kind of also the model that other multiplexing solutions - like HTTP/2 - use in their implementations. The downside is obviously the need for spawning as task, and potential overhead of task-switching which can degrade performance - especially if a multithreaded runtime is used, which would require synchronization between the application and TLS IO task.
It also makes the solution less runtime agnostic, and harder to force-cancel ongoing IO.
The text was updated successfully, but these errors were encountered: