Skip to content

Keep interactive console input for native programs when spawned via the MSYS2 runtime - #138

Merged
dscho merged 7 commits into
mainfrom
fix-nat-handle-leakfix
Jul 27, 2026
Merged

Keep interactive console input for native programs when spawned via the MSYS2 runtime#138
dscho merged 7 commits into
mainfrom
fix-nat-handle-leakfix

Conversation

@dscho

@dscho dscho commented Jul 16, 2026

Copy link
Copy Markdown
Member

I introduced a regression in #131: 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.

Let's fix this.

…ygwin

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 <johannes.schindelin@gmx.de>
@dscho dscho self-assigned this Jul 16, 2026
dscho added 6 commits July 16, 2026 23:52
When native `git.exe` runs a `!` alias under `mintty`, it starts `sh.exe`,
which may in turn launch another native program. A pseudo console
regression made that program see standard input as redirected, causing
console APIs such as `[Console]::KeyAvailable` to fail.

The existing `mintty` grandchild-input test is the right home for a
regression test to catch such a bug because it already launches an
interactive `mintty` session and exercises native/Cygwin/native process
chains, while the Git invocation itself exercises the relevant alias
path.

Keep the process chain attached to `mintty` and use PowerShell's file APIs
to persist the observations. Shell redirection would replace Git's
standard handles before pseudo console setup and mask the regression.
Discard stale results so repeated local runs cannot pass spuriously.

Assisted-by: GPT 5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Use a more robust way to verify the long clone.

Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Prevent cleanup from hanging by directing `exit` to the test's recorded
PowerShell window and waiting for it to close before removing the worktree.

Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Avoid false interrupt timeouts by waiting until the shell alias confirms
`sleep` is running, then reactivating the recorded PowerShell window before
sending Ctrl+C.

Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Avoid false clone timeouts by treating the explicit exit-code marker as
sufficient completion evidence instead of requiring a clean PowerShell
prompt, because the buffer-export hotkey can append `[24~` to it.

Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Windows 2025 runners intermittently drop a single synthesized Ctrl+C or
`exit`. Screen-buffer polling can also miss the short-lived clone
`ssh.exe`, or be polluted by terminal key sequences. A dropped clone
interrupt lets a fast localhost clone complete normally. Interrupted Git
cleanup may also leave harmless empty
`.git/{objects,refs}` scaffolding because Windows delete-pending handles
can make `rmdir` lose a race.

Use exact process ownership, rather than ambient process names or screen
contents, as the invariant. Subscribe before clone launch and identify only
the `ssh.exe` carrying the test's unique key path. Track and revalidate the
exact owned PIDs. Restart and clean up only `sshd` PIDs launched by the test.
Do not assume that unrelated Git, SSH, or `sshd` processes are absent, and
never stop any process by name.

Refocus and reissue Ctrl+C until the exact clone `ssh.exe` exits. Treat
surviving files as failures, but tolerate and remove empty scaffolding.
Reissue `exit` until the recorded window closes. Use an explicit exit-code
marker for the successful clone instead of fragile terminal-output ordering.

Claude Opus 4.8 instrumented dropped input: quick Ctrl+C was delivered in
9/12 attempts and dropped in 3/12. Deliberate key-down/up still dropped
1/12. Its second internal stress attempt passed 20/20.

The committed debug-branch source then independently passed the initial
Windows 2025 20-run gate. Copilot steps were skipped, and no error text
appeared in any iteration log. The ported feature-branch source matches the
tested source except for one behavior-neutral line wrap. It passes the
AutoHotkey parser and hygiene checks.

Assisted-by: Claude Opus 4.8
Assisted-by: GPT-5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
dscho force-pushed the fix-nat-handle-leakfix branch from a781eff to 55abf76 Compare July 19, 2026 20:38
@dscho

dscho commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

/open pr

The workflow run was started

@dscho
dscho merged commit 58d7e46 into main Jul 27, 2026
61 checks passed
@dscho
dscho deleted the fix-nat-handle-leakfix branch July 27, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant