[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