Skip to content

Commit

Permalink
Cygwin: pty: Avoid client deadlock when pty master stops to read.
Browse files Browse the repository at this point in the history
Previsouly, the following commands hangs:
  mintty -e timeout 1 dash -c 'yes aaaaaaaaaaaaaaaaaaaaaaaaa | cat'

The mechanism is as follows.

When the child process (timeout) is terminated, mintty seems to stop
reading pty master even if yes or cat still alive.

If the the pipe used to transfer output from pty slave to pty master
is full due to lack of master reader, WriteFile() to the pipe is
blocked. WriteFile() cannot be canceled by cygwin signal, therefore,
pty slave hangs.

This patch avoids hanging by checking pipe space before calling
WriteFile() and prevents writing data more than space.

Addresses: https://cygwin.com/pipermail/cygwin/2024-June/256178.html
Reported-by: jojelino <[email protected]>
Signed-off-by: Takashi Yano <[email protected]>
  • Loading branch information
tyan0 committed Jul 1, 2024
1 parent 8d8f11b commit c4fb5da
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
25 changes: 22 additions & 3 deletions winsup/cygwin/fhandler/pty.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3118,8 +3118,22 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
return res; /* Discard write data */
while (towrite)
{
ssize_t space = towrite;
if (!is_echo)
{
IO_STATUS_BLOCK iosb = {{0}, 0};
FILE_PIPE_LOCAL_INFORMATION fpli = {0};
NTSTATUS status;

status = NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
FilePipeLocalInformation);
if (!NT_SUCCESS (status))
{
if (towrite < len)
break;
len = -1;
return FALSE;
}
if (ttyp->output_stopped && is_nonblocking)
{
if (towrite < len)
Expand All @@ -3131,13 +3145,18 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
return TRUE;
}
}
while (ttyp->output_stopped)
cygwait (10);
if (ttyp->output_stopped || fpli.WriteQuotaAvailable == 0)
{
cygwait (1);
continue;
}
space = fpli.WriteQuotaAvailable;
}

if (!(ttyp->ti.c_oflag & OPOST)) // raw output mode
{
DWORD n = MIN (OUT_BUFFER_SIZE, towrite);
n = MIN (n, space);
res = WriteFile (h, ptr, n, &n, NULL);
if (!res)
break;
Expand All @@ -3150,7 +3169,7 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
char *buf = (char *)ptr;
DWORD n = 0;
ssize_t rc = 0;
while (n < OUT_BUFFER_SIZE && rc < towrite)
while (n < OUT_BUFFER_SIZE && n < space && rc < towrite)
{
switch (buf[rc])
{
Expand Down
4 changes: 4 additions & 0 deletions winsup/cygwin/release/3.5.4
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ Fixes:

- Fix a problem that ldd command against cygwin DLLs sometimes hangs.
Addresses: https://cygwin.com/pipermail/cygwin/2024-May/255991.html

- Fix a problem that pty slave hangs on writing when pty master stops
to read.
Addresses: https://cygwin.com/pipermail/cygwin/2024-June/256178.html

0 comments on commit c4fb5da

Please sign in to comment.