-
-
Notifications
You must be signed in to change notification settings - Fork 50
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
Using TCP qrexec for updates proxy hangs on EOF #9169
Comments
I tried also setting |
I suspected it might be related to using |
@marmarek got this, working on it. |
use_stdio_socket means that stdin and stdout FDs use the same file description, which is certainly the case for socket-based services. Before this change, it was set to false in this case. This would cause the file description to be made blocking while it is still in use, potentially causing deadlocks. git blame points to commit 0802df7 ("Factor out process_child_io"), but I suspect the actual bug dates back as far as the introduction of socket-based services in 76697e3 ("Working socket-based qrexec"). Fixes: QubesOS/qubes-issues#9169
I'm attaching strace of a failed state: I did the curl call as above, it hanged, and then I interrupted it after several seconds. You can see which FDs are closed and which aren't at that point. |
To trigger this bug, all of the following conditions must be met:
If all four conditions are met, the local caller of The reason that the previous service worked is that after either direction of the channel is shut down, |
The code already has a path for using |
That’s fine, but it will need a new API in libqrexec and a corresponding change in both |
Ugh, I guess it means a change to the |
That’s not guaranteed to work with GCC, as GCC doesn’t initialize padding when a designated initializer is used, though clang does (GCC bug 77992). A cleaner solution is to use |
I guess that would need to be enough, but do document it in the header. And then have an option like |
Is it safe to use |
I'm confused, isn't this exactly the opposite? You said earlier you need to use |
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
The fix (QubesOS/qubes-core-qrexec#151) does some refactoring: instead of calling |
This isn't exactly true: https://github.com/QubesOS/qubes-core-qrexec/blob/main/libqrexec/process_io.c#L195-L214 (but you changed this too) Anyway, |
When qrexec-client-vm is called from a systemd unit connected to a socket, it the same socket on both stdin and stdout. Tell qrexec-client-vm about it, so it can use shutdown() instead of close() to properly deliver EOF. It will also make the qrexec-client-vm to use just stdin FD. QubesOS/qubes-issues#9169
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
You might want to edit this to include the exact commit sha1, so that future viewers do not get confused.
Thanks! I used |
Having to pass manually I’ll write a regression test for this later today. It turns out to be quite easy: send |
Actually, I'd prefer |
This is interesting idea, but I fear it may be unreliable. Explicit request (via SIGUSR1 on the service side, via cmdline switch on the client side) is more reliable. |
For the service side I agree, but why is it unreliable on the client side? I suspect that passing the same file description for both stdin and stdout is uncommon, and that by far the most likely cause is either using |
I agree, "most likely". But in the unlikely case you still end up with a deadlock. |
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
I decided to open-code it, as neither |
What is the unlikely case you are referring to? The one I was referring to is where someone passes the same socket file description for both stdin and stdout and expects to be able to use the socket after |
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
That too - in fact this was the very reason why shutdown was not used for FD 0/1 (linked here before). Lets not re-introduce this issue. |
In fact it is very common - all kind of interactive use does it all the time. If you happen to have a shell connected to a socket instead of TTY, your proposed approach would break stuff. |
1 socket or 2? |
If shell is called with the same socket on stdin/stdout, the child will have the same too. |
Another option might be to have a service configuration option to exit if either side of the communication channel closes, rather than waiting until both close. That would allow QubesOS/qubes-core-agent-linux#495 to be merged without breaking existing Whonix VMs (e.g. restored from backup). |
It is critical that EOF from the service gets propagated to the stdout of qrexec-client-vm or qrexec-client. Otherwise, the caller will not recognize that EOF has happened and may wait forever for data that will never arrive. In QubesOS/qubes-issues#9169, this caused curl to hang when reading a plaintext HTTP/1.0 response from tinyproxy, which relies on EOF to delimit the response. The bug will trigger iff all of the following conditions are met: 1. The remote side must close the writing side of the connection, either by closing or shutting down a socket. 2. If the remote service is executable, it must _not_ exit. 3. qrexec-client-vm or qrexec-client must _not_ get EOF on stdin. 4. Either the remote side does not close its stdin, or the local side does not continue to write data. 5. The same file description is used for both stdin and stdout of the local qrexec-client or qrexec-client-vm process. 6. qrexec-client or qrexec-client-vm connect the stdin and stdout of the remote process to their own stdin and stdout, not the stdin and stdout of a locally spawned command. To fix this bug, add flags to qrexec-client and qrexec-client-vm that cause the socket passed as file descriptor 0 to be used for both stdin and stdout. Fixes: QubesOS/qubes-issues#9169
The remaining work is tracked by #9176. |
When qrexec-client-vm is called from a systemd unit connected to a socket, it the same socket on both stdin and stdout. Tell qrexec-client-vm about it, so it can use shutdown() instead of close() to properly deliver EOF. It will also make the qrexec-client-vm to use just stdin FD. QubesOS/qubes-issues#9169
When qrexec-client-vm is called from a systemd unit connected to a socket, it the same socket on both stdin and stdout. Tell qrexec-client-vm about it, so it can use shutdown() instead of close() to properly deliver EOF. It will also make the qrexec-client-vm to use just stdin FD. QubesOS/qubes-issues#9169
How to file a helpful issue
Qubes OS release
R4.2
Brief summary
Normal updates proxy operation works, but getting the "filtered" page (used by whonix to check if updates proxy uses tor or not) hangs. The issue does not happen when using executable service.
Steps to reproduce
curl -v http://127.0.0.1:8082/
Expected behavior
Get the 403 message and curl terminates
Actual behavior
Get the 403 message, but curl remain waiting.
The issue is most likely related to HTTP 1.0 - and curl waiting for EOF (with HTTP 1.1 it doesn't need to wait).
I guess it may be caused by #9142, but I'm opening separate issue anyway, because I'm not sure.
The text was updated successfully, but these errors were encountered: