[newlib-cygwin] Cygwin: pty: Guard to_be_read_from_nat_pipe() by pipe_sw_mutex
Takashi Yano
tyan0@sourceware.org
Sun Mar 29 00:44:10 GMT 2026
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=2f705b879fac45e5cd761fad8dd7de6b388aee2a
commit 2f705b879fac45e5cd761fad8dd7de6b388aee2a
Author: Takashi Yano <takashi.yano@nifty.ne.jp>
Date: Tue Mar 17 13:11:44 2026 +0900
Cygwin: pty: Guard to_be_read_from_nat_pipe() by pipe_sw_mutex
`to_be_read_from_nat_pipe()` reads several shared-memory fields
(`switch_to_nat_pipe`, `pcon_activated`, `pty_input_state`) to
decide whether keystrokes should go to the nat pipe. It is called
from `master::write()` on every keystroke. Without synchronization,
the slave can be in the middle of a pipe switch (changing these
fields in `setup_for_non_cygwin_app()`, `cleanup_for_non_cygwin_app()`,
or `setpgid_aux()`) while the master reads a half-updated snapshot,
making an inconsistent routing decision that sends keystrokes to the
wrong pipe.
Guard `to_be_read_from_nat_pipe()` with `pipe_sw_mutex` so it
always reads a consistent state. The spin-wait at entry handles the
pseudo console initialization case: when `pipe_sw_mutex` is held by
the slave during `setup_pseudoconsole()` and `pcon_start` is set,
the function returns false immediately, routing keystrokes to the
cyg pipe through `line_edit()` where the CSI6n response handler
expects them.
Acquiring `pipe_sw_mutex` inside `to_be_read_from_nat_pipe()`
creates a lock ordering constraint: `master::write()` holds
`input_mutex` before calling `to_be_read_from_nat_pipe()`, so the
master's lock order is `input_mutex` then `pipe_sw_mutex`.
Previously, `cleanup_for_non_cygwin_app()` and `setpgid_aux()`
acquired `pipe_sw_mutex` first and then `input_mutex` (for
`transfer_input()`), which is the reverse order and would deadlock.
Restructure both functions to release `pipe_sw_mutex` before
acquiring `input_mutex`, maintaining a consistent lock order
throughout.
Fixes: bb4285206207 ("Cygwin: pty: Implement new pseudo console support.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Diff:
---
winsup/cygwin/fhandler/pty.cc | 48 ++++++++++++++++++++++++++++++-------------
1 file changed, 34 insertions(+), 14 deletions(-)
diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index ac5f67bc4..18285b874 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -1284,22 +1284,42 @@ fhandler_pty_slave::mask_switch_to_nat_pipe (bool mask, bool xfer)
bool
fhandler_pty_common::to_be_read_from_nat_pipe (void)
{
+ /* If the slave is in setup_pseudoconsole(), pipe_sw_mutex cannot
+ be acquired because the slave has it. In this case pcon_start
+ will be asserted. During pcon_start, other input than response
+ to CSI6n should be go to cyg-pipe. So, wait for pcon_start and
+ return false. */
+ while (WaitForSingleObject (pipe_sw_mutex, 0) == WAIT_TIMEOUT)
+ if (get_ttyp ()->pcon_start || get_ttyp ()->pcon_start_pid)
+ return false;
+ else
+ yield ();
+
+ bool ret = false;
if (!get_ttyp ()->switch_to_nat_pipe)
- return false;
+ goto out;
- char name[MAX_PATH];
- shared_name (name, TTY_SLAVE_READING, get_minor ());
- HANDLE masked = OpenEvent (READ_CONTROL, FALSE, name);
- CloseHandle (masked);
+ {
+ char name[MAX_PATH];
+ shared_name (name, TTY_SLAVE_READING, get_minor ());
+ HANDLE masked = OpenEvent (READ_CONTROL, FALSE, name);
+ CloseHandle (masked);
- if (masked) /* The foreground process is cygwin process */
- return false;
+ if (masked) /* The foreground process is cygwin process */
+ goto out;
+ }
if (!pinfo (get_ttyp ()->getpgid ()))
/* GDB may set invalid process group for non-cygwin process. */
- return true;
+ {
+ ret = true;
+ goto out;
+ }
- return get_ttyp ()->nat_fg (get_ttyp ()->getpgid ());
+ ret = get_ttyp ()->nat_fg (get_ttyp ()->getpgid ());
+out:
+ ReleaseMutex (pipe_sw_mutex);
+ return ret;
}
void
@@ -3941,7 +3961,6 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
goto maybe_dumb;
/* Check if terminal has CSI6n */
- WaitForSingleObject (pipe_sw_mutex, INFINITE);
WaitForSingleObject (input_mutex, mutex_timeout);
/* Set pcon_activated and pcon_start so that the response
will sent to io_handle_nat rather than io_handle. */
@@ -3977,7 +3996,6 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
while (len);
get_ttyp ()->pcon_activated = false;
get_ttyp ()->nat_pipe_owner_pid = 0;
- ReleaseMutex (pipe_sw_mutex);
if (len == 0)
goto not_has_csi6n;
@@ -3993,7 +4011,6 @@ not_has_csi6n:
get_ttyp ()->pcon_start = false;
get_ttyp ()->pcon_activated = false;
ReleaseMutex (input_mutex);
- ReleaseMutex (pipe_sw_mutex);
maybe_dumb:
get_ttyp ()->pcon_cap_checked = true;
return false;
@@ -4314,7 +4331,6 @@ fhandler_pty_slave::cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp,
DWORD force_switch_to)
{
ttyp->wait_fwd ();
- WaitForSingleObject (p->pipe_sw_mutex, INFINITE);
if (nat_pipe_owner_self (ttyp->nat_pipe_owner_pid))
{
DWORD switch_to = get_winpid_to_hand_over (ttyp, force_switch_to);
@@ -4330,6 +4346,7 @@ fhandler_pty_slave::cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp,
ReleaseMutex (p->input_mutex);
}
}
+ WaitForSingleObject (p->pipe_sw_mutex, INFINITE);
if (ttyp->pcon_activated)
close_pseudoconsole (ttyp, force_switch_to);
else
@@ -4348,6 +4365,7 @@ fhandler_pty_slave::setpgid_aux (pid_t pid)
if (!was_nat_fg && nat_fg && get_ttyp ()->switch_to_nat_pipe
&& get_ttyp ()->pty_input_state_eq (tty::to_cyg))
{
+ ReleaseMutex (pipe_sw_mutex);
WaitForSingleObject (input_mutex, mutex_timeout);
acquire_attach_mutex (mutex_timeout);
transfer_input (tty::to_nat, get_handle (), get_ttyp (),
@@ -4358,6 +4376,7 @@ fhandler_pty_slave::setpgid_aux (pid_t pid)
else if (was_nat_fg && !nat_fg && get_ttyp ()->switch_to_nat_pipe
&& get_ttyp ()->pty_input_state_eq (tty::to_nat))
{
+ ReleaseMutex (pipe_sw_mutex);
bool attach_restore = false;
HANDLE from = get_handle_nat ();
DWORD resume_pid = 0;
@@ -4385,7 +4404,8 @@ fhandler_pty_slave::setpgid_aux (pid_t pid)
release_attach_mutex ();
ReleaseMutex (input_mutex);
}
- ReleaseMutex (pipe_sw_mutex);
+ else
+ ReleaseMutex (pipe_sw_mutex);
}
bool
More information about the Cygwin-cvs
mailing list