Re: [PATCH] Cygwin: pty: keep interactive console input for native programs via Cygwin
Johannes Schindelin <[email protected]> Wed, 22 Jul 2026 01:18:16 +0200 (CEST)
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <[email protected]> |
Hi Takashi, On Tue, 21 Jul 2026, Takashi Yano wrote: > On Mon, 20 Jul 2026 09:43:18 +0000 > "Johannes Schindelin wrote: > > From: Johannes Schindelin <[email protected]> > >=20 > > 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. > >=20 > > 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: > >=20 > > git -c 'alias.console-probe=3D!powershell.exe -NoLogo -NoProfile -Co= mmand " > > Write-Output ([Console]::IsInputRedirected) > > try { > > [void][Console]::KeyAvailable > > exit 0 > > } catch { > > exit 1 > > } > > "' console-probe > >=20 > > Running this command with a Win32 version of `git.exe` currently print= s > > `True` and exits with exit code 1. In the latest official release, whe= re > > this bug is not present, it prints `False` and results in exit code 0. > >=20 > > 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. > >=20 > > `open_with_arch()` calls `open()`, copies the first pty fhandler's sta= te > > 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. > >=20 > > 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 val= ue > > closes the new handle instead. The nested native program then receives > > unusable console input. > >=20 > > Preserve usable console input by moving the unchanged transactional pc= on > > handle adoption to `open()`, before the archetype snapshot. The archet= ype > > then receives valid pcon handles, all pty fd fhandlers inherit live > > handles, and the superseded raw pipe handles are closed exactly once. > >=20 > > This commit is best viewed with `--color-moved`. > >=20 > > 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]> >=20 > How can I reproduce the issue in cygwin? > I tried: > $ git -c 'alias.console-probe=3D!powershell.exe -NoLogo -NoProfile -Comm= and " > Write-Output ([Console]::IsInputRedirected) > try { > [void][Console]::KeyAvailable > exit 0 > } catch { > exit 1 > } > "' console-probe > False > $ '/cygdrive/c/Program Files/Git/mingw64/bin/git.exe' -c 'alias.console-= probe=3D!powershell.exe -NoLogo -NoProfile -Command " > Write-Output ([Console]::IsInputRedirected) > try { > [void][Console]::KeyAvailable > exit 0 > } catch { > exit 1 > } > "' console-probe > False > $ '/cygdrive/c/Program Files/Git/bin/git.exe' -c 'alias.console-probe= =3D!powershell.exe -NoLogo -NoProfile -Command " > Write-Output ([Console]::IsInputRedirected) > try { > [void][Console]::KeyAvailable > exit 0 > } catch { > exit 1 > } > "' console-probe > False > $ >=20 > All look successfull on master branch... I'm sorry, I should have clarified several things: - First of all, this reproducer has to be run in MinTTY. - Second, it does _not_ actually reproduce the bug with the Cygwin runtime built from cygwin/master. It only reproduces in Git for Windows' flavor of the MSYS2 runtime because of the backports of: - 6eed1ef748 (Cygwin: pty: detect pcon-backed pty for non-Cygwin-spawned children, 2026-04-30) - b34394d456 (Cygwin: pty: Fixup pty state after a cygwin app exits, 2026-06-13) - 60a88896dc (Cygwin: pty: do not leak nat handles when adopting the pcon's in open_setup(), 2026-06-25) The combination of these three commits is the trifecta that surfaces that bug, and the same issue _does_ reproduce when cherry-picking those backports on top of `cygwin-3_6-branch`. For your convenience, I have pushed that branch to https://github.com/dscho/msys2-runtime/commits/minimal-backports-for-nat= -leakfix-bug - The reproducer not only requires the use of Git for Windows' `git.exe` (as an easy way to run a Win32 program that calls a non-Win32 Bash that in turn calls a Win32 program again, I should probably have come up with a command-line that uses powershell.exe instead of git.exe). In addition, `git.exe`'s _strong_ preference for _its own_ `sh.exe` needs to be side-stepped by defining `MSYSTEM=3DMINGW64` (which stops that PATH-editing logic of `git.exe`): MSYSTEM=3DMINGW64 PATH=3D/cygdrive/c/Program\ Files/Git/mingw64/bin:/usr= /bin:$PATH git -c 'alias.console-probe=3D!powershell.exe -NoLogo -NoProfil= e -Command "Write-Output ([Console]::IsInputRedirected); try { [void][Cons= ole]::KeyAvailable; exit 0} catch {exit 1}"' console-probe However, as I mentioned, this does not reproduce the bug on `cygwin/master`. The reason is that bbd3710fc8 (Cygwin: console: Set console mode only if std{in,out,err} is console, 2025-07-03) _hides_ the part of the bug that the reproducer exercises. But it does not _fix_ the bug, it's just harder to trigger. The fix I proposed is still necessary: In `open_with_arch()` the order is: `open()` -> archetype snapshot (`copy_from`) -> `open_setup()`. For a pcon-activated pty, `open()` installs the cyg master-side duplicates into the two nat slots; the archetype then snapshots those slots; then `open_setup()` (as written by `60a88896dc`) closes those two handles and installs the pcon duplicates in their place. So the shared, long-lived archetype is left owning two handle values that were just closed. The next descriptor opened for the same pty takes `copy_from(archetype)`, inherits those two closed values, and its own `open_setup()` calls `CloseHandle` on them again: a close of an already-closed, hence possibly-reused, handle. Worse, in that same `open_setup()` the fresh pcon duplications are allocated while those values are free, so one can be handed back the very value that's about to be closed. That's a "close a stale value, kill a live handle" failure, and nothing in master's structure prevents it. Ciao, Johannes