[PATCH] Cygwin: pty: keep interactive console input for native programs via Cygwin

"Johannes Schindelin via GitGitGadget" <[email protected]> Mon, 20 Jul 2026 09:43:18 +0000
Newsgroups gmane.os.cygwin.patches
Message-ID <[email protected]>
From: Johannes Schindelin <[email protected]>

Currently, when a native Windows program starts a Cygwin program while a
pseudo console is active, and the Cygwin program then starts another
native Windows program, the final program can lose access to console
input. It then behaves as though its standard input were redirected
instead of remaining interactive.

For example, a native `git.exe` may invoke shell aliases (i.e. execute a
shell command) that would in turn call interactive Git commands who
would no longer work because their standard input appeared to be
redirected. This can be demonstrated as follows:

  git -c 'alias.console-probe=!powershell.exe -NoLogo -NoProfile -Command "
    Write-Output ([Console]::IsInputRedirected)
    try {
      [void][Console]::KeyAvailable
      exit 0
    } catch {
      exit 1
    }
  "' console-probe

Running this command with a Win32 version of `git.exe` currently prints
`True` and exits with exit code 1. In the latest official release, where
this bug is not present, it prints `False` and results in exit code 0.

The reason is to be fonud in the archetype code. Reminder: For each
pseudo terminal (pty), the archetype is the shared pty fhandler that
owns the underlying native handles and supplies them to every
per-file-descriptor fhandler for that pty.

`open_with_arch()` calls `open()`, copies the first pty fhandler's state
into the archetype, and then calls `open_setup()`. At that stage, pcon
handle adoption already took place in `open_setup()`. This was not
anticipated by 60a88896dc (Cygwin: pty: do not leak nat handles when
adopting the pcon's in open_setup(), 2026-06-25), which tried to fix a
leak by closing the superseded native handles as they were replaced in
`open_setup()`.  Because `open_with_arch()` had already copied those
handle values into the archetype, closing them invalidated the
archetype's copies.

The archetype therefore retained stale values for those closed handles,
which later pty fd fhandlers would inherit. If Windows reuses one of
those values for a newly duplicated pcon handle, closing the stale value
closes the new handle instead. The nested native program then receives
unusable console input.

Preserve usable console input by moving the unchanged transactional pcon
handle adoption to `open()`, before the archetype snapshot. The archetype
then receives valid pcon handles, all pty fd fhandlers inherit live
handles, and the superseded raw pipe handles are closed exactly once.

This commit is best viewed with `--color-moved`.

Fixes: 60a88896dce0 ("Cygwin: pty: do not leak nat handles when
 adopting the pcon's in open_setup()")
Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <[email protected]>
---
    Fix a regression on cygwin/master
    
    I noticed this regression in Git for Windows' fork, to which I had
    backported the faulty commit from Cygwin's master branch.

Published-As: https://github.com/cygwingitgadget/cygwin/releases/tag/pr-8%2Fdscho%2Ffix-nat-handle-leakfix-cygwin-v1
Fetch-It-Via: git fetch https://github.com/cygwingitgadget/cygwin pr-8/dscho/fix-nat-handle-leakfix-cygwin-v1
Pull-Request: https://github.com/cygwingitgadget/cygwin/pull/8

 winsup/cygwin/fhandler/pty.cc | 44 +++++++++++++++++------------------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index f3df55f34..37a480933 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -1220,26 +1220,6 @@ fhandler_pty_slave::open (int flags, mode_t)
       release_attach_mutex ();
     }
 
-  set_open_status ();
-  return 1;
-
-err:
-  if (GetLastError () == ERROR_FILE_NOT_FOUND)
-    set_errno (ENXIO);
-  else
-    __seterrno ();
-err_no_errno:
-  termios_printf (errmsg);
-err_no_msg:
-  for (HANDLE **h = handles; *h; h++)
-    if (**h && **h != INVALID_HANDLE_VALUE)
-      CloseHandle (**h);
-  return 0;
-}
-
-bool
-fhandler_pty_slave::open_setup (int flags)
-{
   if (get_ttyp ()->pcon_activated)
     {
       HANDLE pcon_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
@@ -1255,8 +1235,8 @@ fhandler_pty_slave::open_setup (int flags)
 				        0, TRUE, DUPLICATE_SAME_ACCESS);
 	  if (ok_in && ok_out)
 	    {
-	      /* Close the cyg master-side handles open() installed before
-		 replacing them, so they do not leak. */
+	      /* Replace these before open_with_arch() copies them into the
+		 archetype shared by all pty slave fhandlers. */
 	      CloseHandle (get_handle_nat ());
 	      CloseHandle (get_output_handle_nat ());
 	      set_handle_nat (new_in);
@@ -1273,6 +1253,26 @@ fhandler_pty_slave::open_setup (int flags)
 	}
     }
 
+  set_open_status ();
+  return 1;
+
+err:
+  if (GetLastError () == ERROR_FILE_NOT_FOUND)
+    set_errno (ENXIO);
+  else
+    __seterrno ();
+err_no_errno:
+  termios_printf (errmsg);
+err_no_msg:
+  for (HANDLE **h = handles; *h; h++)
+    if (**h && **h != INVALID_HANDLE_VALUE)
+      CloseHandle (**h);
+  return 0;
+}
+
+bool
+fhandler_pty_slave::open_setup (int flags)
+{
   set_flags ((flags & ~O_TEXT) | O_BINARY);
   myself->set_ctty (this, flags);
   report_tty_counts (this, "opened", "");

base-commit: 524d75ff73986b263161665af771cc90e55b5e01
-- 
cygwingitgadget