Skip to content

Commit

Permalink
Send EOF whenever closing stdout
Browse files Browse the repository at this point in the history
This is expected by the protocol.
  • Loading branch information
DemiMarie committed May 22, 2024
1 parent 50bc9db commit 0701529
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
5 changes: 5 additions & 0 deletions libqrexec/process_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ int qrexec_process_io(const struct process_io_request *req,
* local FDs. However, don't exit yet, because there might
* still be some data in stdin_buf waiting to be flushed.
*/
if (stdout_fd != -1) {
/* Send EOF */
struct msg_header hdr = { .type = stdout_msg_type, .len = 0, };
libvchan_send(vchan, &hdr, (int)sizeof(hdr));
}
close_stdout();
close_stderr(stderr_fd);
stderr_fd = -1;
Expand Down
37 changes: 35 additions & 2 deletions qrexec/tests/socket/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,37 @@ def test_run_client_bidirectional_shutdown(self):
remote.close()
local.close()

def test_run_client_bidirectional_shutdown_early_exit(self):
try:
remote, local = socket.socketpair()
target_client = self.run_service(stdio=remote)
initial_data = b"stdout data\n"
target_client.send_message(qrexec.MSG_DATA_STDOUT, initial_data)
# FIXME: data can be received in multiple messages
self.assertEqual(local.recv(len(initial_data)), initial_data)
initial_data = b"stdin data\n"
local.sendall(initial_data)
self.assertStdoutMessages(target_client, initial_data, qrexec.MSG_DATA_STDIN)
target_client.send_message(qrexec.MSG_DATA_STDOUT, b"")
# Check that EOF got propagated on this side too, even though
# we still have a reference to the socket. This indicates that
# qrexec-client-vm shut down the socket for writing.
self.assertEqual(local.recv(1), b"")
with self.assertRaises(BrokenPipeError):
remote.send(b"a")
target_client.send_message(
qrexec.MSG_DATA_EXIT_CODE, struct.pack("<L", 42)
)
local.shutdown(socket.SHUT_WR)
# Check that EOF received
self.assertEqual(target_client.recv_message(), (qrexec.MSG_DATA_STDIN, b""))
self.client.wait()
self.assertEqual(self.client.returncode, 42)
finally:
remote.close()
local.close()


def test_run_client_replace_chars(self):
target_client = self.run_service(options=["-t"])
target_client.send_message(
Expand Down Expand Up @@ -1448,7 +1479,8 @@ def test_run_client_failed(self):
)
# there should be no MSG_DATA_EXIT_CODE from qrexec-client-vm
# and also no MSG_DATA_STDIN after receiving MSG_DATA_EXIT_CODE
self.assertListEqual(target_client.recv_all_messages(), [])
self.assertListEqual(target_client.recv_all_messages(),
[(qrexec.MSG_DATA_STDIN, b"")])
self.assertEqual(self.client.stdout.read(), b"")
self.client.wait()
self.assertEqual(self.client.returncode, qrexec.QREXEC_EXIT_PROBLEM)
Expand Down Expand Up @@ -1538,7 +1570,8 @@ def test_run_client_with_local_proc_service_failed(self):
qrexec.MSG_DATA_EXIT_CODE, struct.pack("<L", qrexec.QREXEC_EXIT_PROBLEM)
)
# there should be no MSG_DATA_EXIT_CODE from qrexec-client-vm
self.assertListEqual(target_client.recv_all_messages(), [])
self.assertListEqual(target_client.recv_all_messages(),
[(qrexec.MSG_DATA_STDIN, b"")])
target_client.close()
self.assertEqual(self.client.stdout.read(), b"")
self.client.wait()
Expand Down

0 comments on commit 0701529

Please sign in to comment.