From df250904469ac74c79c845a35c6cb9ca7acb126e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Tue, 10 Dec 2024 22:36:46 +0100 Subject: [PATCH] Add a test for qrexec-client race condition Check if the service exit code is correctly retrieved even if the the service terminates at the exact moment the qrexec-client tries to send some data. Try to win the race by initially sending SIGSTOP to the qrexec-client process, and sending SIGCONT only after preparing both local and remote data streams. qrexec-client will handle local data stream first, at which point remote socket is already closed. Similar issue applies to qrexec-client-vm, but since the implementation is shared, one test is enough. QubesOS/qubes-issues#9618 --- qrexec/tests/socket/daemon.py | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/qrexec/tests/socket/daemon.py b/qrexec/tests/socket/daemon.py index 2b6b9dea..d561e740 100644 --- a/qrexec/tests/socket/daemon.py +++ b/qrexec/tests/socket/daemon.py @@ -28,6 +28,7 @@ import time import itertools import socket +import signal import psutil import pytest @@ -664,6 +665,7 @@ def start_client(self, args): self.client = subprocess.Popen( cmd, env=env, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, ) self.addCleanup(self.stop_client) @@ -744,6 +746,65 @@ def test_run_vm_command_from_dom0(self): self.client.wait() self.assertEqual(self.client.returncode, 42) + def test_run_vm_command_from_dom0_reject_stdin(self): + """Test if qrexec-client properly returns remote exit code even if + service didn't read all of stdin""" + cmd = "user:command" + target_domain_name = "target_domain" + target_domain_uuid = "d95e1147-2d82-4595-90bb-5a7500cc3196" + target_domain = 42 + target_port = 513 + + target_daemon = self.connect_daemon( + target_domain, target_domain_name, target_domain_uuid + ) + self.start_client(["-d", target_domain_name, cmd]) + target_daemon.accept() + target_daemon.handshake() + + # negotiate_connection_params + self.assertEqual( + target_daemon.recv_message(), + ( + qrexec.MSG_EXEC_CMDLINE, + struct.pack("