[PATCH v2] Cygwin: pty: Restore nat handles in all PTY-slave instances in GDB
Johannes Schindelin
Johannes.Schindelin@gmx.de
Mon Apr 6 08:14:10 GMT 2026
Hi Takashi,
On Mon, 6 Apr 2026, Takashi Yano wrote:
> If non-cygwin app is started in GDB and terminating it normally,
> re-running the non-cygwin app might fail in setup_pseudoconsole().
>
> The error is something like:
>
> $ gdb ./winsleep
> GNU gdb (GDB) (Cygwin 15.2-1) 15.2
> Copyright (C) 2024 Free Software Foundation, Inc.
> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
> This is free software: you are free to change and redistribute it.
> There is NO WARRANTY, to the extent permitted by law.
> Type "show copying" and "show warranty" for details.
> This GDB was configured as "x86_64-pc-cygwin".
> Type "show configuration" for configuration details.
> For bug reporting instructions, please see:
> <https://www.gnu.org/software/gdb/bugs/>.
> Find the GDB manual and other documentation resources online at:
> <http://www.gnu.org/software/gdb/documentation/>.
>
> For help, type "help".
> Type "apropos word" to search for commands related to "word"...
> Reading symbols from ./winsleep...
> (gdb) run
> Starting program: /home/yano/winsleep
> [New Thread 49324.0x14178]
> [Thread 49324.0x14178 exited with code 0]
> [Inferior 1 (process 49324) exited normally]
> (gdb) run
> Starting program: /home/yano/winsleep
> 0 [] gdb 294 fhandler_pty_slave::setup_pseudoconsole: CreatePseudoConsole() failed. 00000057 80070057
> [New Thread 86480.0xfd4]
> [Thread 86480.0xfd4 exited with code 0]
> [Inferior 1 (process 86480) exited normally]
> (gdb)
>
> The essential problem is lack of restoring nat handles for *ALL* the
> PTY-slave instances after closing pseudo console in GDB.
>
> Restoring handles from pseudo console handles to simple pipe handles
> is not necessary in normal non-cygwin apps because pseudo console is
> setup in the stub process for the non-cygwin app and the stub process
> exits after the app is terminated.
>
> However, for GDB, pseudo console is setup in GDB process in hooked
> CreateProcess() because GDB does not use exec() to run an inferior
> (debuggee). Therefore, after the inferior exits, nat handle must be
> restored to simple pipe handles.
>
> The current code restores only handles in the PTY-slave instance
> that has called fhandler_pty_slave::reset_switch_to_nat_pipe(). If
> this instance is different from the instance that will setup pseudo
> console, the nat handles are not restored correctly, then call to
> CreatePseudoConsole() causes error.
The fix is correct and the commit message is excellent: detailed repro,
clear root cause analysis, and a good explanation of why GDB is the
special case (it sets up the pseudo console in its own process rather
than in a stub that exits). Thank you!
Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>
One tiny typo in the commit message:
> To solves this issue, restore nat handles in all the PTY-slave
> instances to simple pipe handles when the inferior exits with this
> patch.
"To solves" should be "To solve". Since you apply your own patches, you
can fix that before pushing.
> In addition, if ctty is PTY-slave, fixup handles in it as well.
One optional suggestion for a possible follow-up: after this patch, the
handle-replacement pattern (iterate the fd table + ctty, compare old
handles, set new handles, close old handles) is now duplicated nearly
verbatim between `reset_switch_to_nat_pipe()` and `setup_pseudoconsole()`.
The only difference between the two blocks is the two new HANDLE values
passed in.
This would be a natural candidate for a small helper method, something
like this:
void
fhandler_pty_slave::replace_nat_handles (HANDLE new_input, HANDLE new_output)
{
HANDLE orig_input = get_handle_nat ();
HANDLE orig_output = get_output_handle_nat ();
cygheap_fdenum cfd (false);
while (cfd.next () >= 0)
if (cfd->get_device () == get_device ())
{
fhandler_pty_slave *ptys = (fhandler_pty_slave *) (fhandler_base *) cfd;
if (ptys->get_handle_nat () == orig_input)
ptys->set_handle_nat (new_input);
if (ptys->get_output_handle_nat () == orig_output)
ptys->set_output_handle_nat (new_output);
}
if (cygheap->ctty->get_device () == get_device ())
{
fhandler_pty_slave *ptys = (fhandler_pty_slave *) cygheap->ctty;
if (ptys->get_handle_nat () == orig_input)
ptys->set_handle_nat (new_input);
if (ptys->get_output_handle_nat () == orig_output)
ptys->set_output_handle_nat (new_output);
}
CloseHandle (orig_input);
CloseHandle (orig_output);
}
Both call sites would then collapse to a single line each. This is not a
request to respin; the patch is fine as-is. Just something worth
considering as a follow-up since the duplication is quite substantial
(~20 lines).
Ciao,
Johannes
> Fixes: 8aeb3f3e5037 ("Cygwin: pty: Make apps using console APIs be able to debug with gdb.")
> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> Reviewed-by:
> ---
> winsup/cygwin/fhandler/pty.cc | 53 +++++++++++++++++++++++++++++------
> 1 file changed, 44 insertions(+), 9 deletions(-)
>
> diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
> index 9868e88e5..0717c043b 100644
> --- a/winsup/cygwin/fhandler/pty.cc
> +++ b/winsup/cygwin/fhandler/pty.cc
> @@ -1118,6 +1118,8 @@ fhandler_pty_slave::reset_switch_to_nat_pipe (void)
> else
> hand_over_only (get_ttyp ());
> ReleaseMutex (pipe_sw_mutex);
> +
> + HANDLE input_handle_nat, output_handle_nat;
> if (need_restore_handles)
> {
> pinfo p (get_ttyp ()->master_pid);
> @@ -1125,16 +1127,15 @@ fhandler_pty_slave::reset_switch_to_nat_pipe (void)
> OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->dwProcessId);
> if (pty_owner)
> {
> - CloseHandle (get_handle_nat ());
> DuplicateHandle (pty_owner,
> get_ttyp ()->from_master_nat (),
> - GetCurrentProcess (), &get_handle_nat (),
> + GetCurrentProcess (),
> + &input_handle_nat,
> 0, TRUE, DUPLICATE_SAME_ACCESS);
> - CloseHandle (get_output_handle_nat ());
> DuplicateHandle (pty_owner,
> get_ttyp ()->to_master_nat (),
> GetCurrentProcess (),
> - &get_output_handle_nat (),
> + &output_handle_nat,
> 0, TRUE, DUPLICATE_SAME_ACCESS);
> CloseHandle (pty_owner);
> }
> @@ -1154,11 +1155,37 @@ fhandler_pty_slave::reset_switch_to_nat_pipe (void)
> CloseHandle (repl.to_master); /* not used. */
> CloseHandle (repl.to_slave_nat); /* not used. */
> CloseHandle (repl.to_slave); /* not used. */
> - CloseHandle (get_handle_nat ());
> - set_handle_nat (repl.from_master_nat);
> - CloseHandle (get_output_handle_nat ());
> - set_output_handle_nat (repl.to_master_nat);
> + input_handle_nat = repl.from_master_nat;
> + output_handle_nat = repl.to_master_nat;
> }
> +
> + /* Restore nat handles in all pty slave instances */
> + HANDLE orig_input_handle_nat = get_handle_nat();
> + HANDLE orig_output_handle_nat = get_output_handle_nat();
> + cygheap_fdenum cfd (false);
> + while (cfd.next () >= 0)
> + if (cfd->get_device () == get_device ())
> + {
> + fhandler_base *fh = cfd;
> + fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
> + if (ptys->get_handle_nat () == orig_input_handle_nat)
> + ptys->set_handle_nat (input_handle_nat);
> + if (ptys->get_output_handle_nat () ==
> + orig_output_handle_nat)
> + ptys->set_output_handle_nat (output_handle_nat);
> + }
> + if (cygheap->ctty->get_device () == get_device ())
> + {
> + fhandler_pty_slave *ptys =
> + (fhandler_pty_slave *) cygheap->ctty;
> + if (ptys->get_handle_nat () == orig_input_handle_nat)
> + ptys->set_handle_nat (input_handle_nat);
> + if (ptys->get_output_handle_nat () ==
> + orig_output_handle_nat)
> + ptys->set_output_handle_nat (output_handle_nat);
> + }
> + CloseHandle (orig_input_handle_nat);
> + CloseHandle (orig_output_handle_nat);
> }
> myself->exec_dwProcessId = 0;
> isHybrid = false;
> @@ -3465,7 +3492,7 @@ fhandler_pty_slave::setup_pseudoconsole ()
> skip_create:
> do
> {
> - /* Fixup handles */
> + /* Fixup handles in all PTY-slave instances */
> HANDLE orig_input_handle_nat = get_handle_nat ();
> HANDLE orig_output_handle_nat = get_output_handle_nat ();
> cygheap_fdenum cfd (false);
> @@ -3479,6 +3506,14 @@ skip_create:
> if (ptys->get_output_handle_nat () == orig_output_handle_nat)
> ptys->set_output_handle_nat (hpConOut);
> }
> + if (cygheap->ctty->get_device () == get_device ())
> + {
> + fhandler_pty_slave *ptys = (fhandler_pty_slave *) cygheap->ctty;
> + if (ptys->get_handle_nat () == orig_input_handle_nat)
> + ptys->set_handle_nat (hpConIn);
> + if (ptys->get_output_handle_nat () == orig_output_handle_nat)
> + ptys->set_output_handle_nat (hpConOut);
> + }
> CloseHandle (orig_input_handle_nat);
> CloseHandle (orig_output_handle_nat);
> }
> --
> 2.51.0
>
>
>
More information about the Cygwin-patches
mailing list