Skip to content

Commit e1eb9c1

Browse files
jburgess777philpep
authored andcommitted
Prevent Paramiko deadlock when test sends more than 2MB to stdout
The paramiko library has a known problem where checking the exit status can cause a deadlock if the command has written a lot of output to the stdout (or stderr) channel: https://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.recv_exit_status We can work around this, for the stdout case, by reading the data before checking the exit status.
1 parent fa48a25 commit e1eb9c1

File tree

2 files changed

+10
-1
lines changed

2 files changed

+10
-1
lines changed

test/test_backends.py

+9
Original file line numberDiff line numberDiff line change
@@ -640,3 +640,12 @@ def test_get_hosts():
640640
("a", 10),
641641
("a", 1),
642642
]
643+
644+
645+
@pytest.mark.testinfra_hosts(*HOSTS)
646+
def test_command_deadlock(host):
647+
# Test for deadlock when exceeding Paramiko transport buffer (2MB)
648+
# https://docs.paramiko.org/en/latest/api/channel.html#paramiko.channel.Channel.recv_exit_status
649+
size = 3 * 1024 * 1024
650+
output = host.check_output(f"python3 -c 'print(\"a\" * {size})'")
651+
assert len(output) == size

testinfra/backend/paramiko.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ def _exec_command(self, command: bytes) -> tuple[int, bytes, bytes]:
138138
if self.get_pty:
139139
chan.get_pty()
140140
chan.exec_command(command)
141-
rc = chan.recv_exit_status()
142141
stdout = b"".join(chan.makefile("rb"))
143142
stderr = b"".join(chan.makefile_stderr("rb"))
143+
rc = chan.recv_exit_status()
144144
return rc, stdout, stderr
145145

146146
def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult:

0 commit comments

Comments
 (0)