[PATCH v2] Cygwin: console: Abort setting disable_master_thread when no con.owner

Johannes Schindelin Johannes.Schindelin@gmx.de
Sun Sep 13 12:12:16 GMT 2026


Hi Takashi,

On Sat, 12 Sep 2026, Takashi Yano wrote:

> With the commit 733d5a953fa9 ("Cygwin: console: Ensure the master
> thread runs only when it is supposed to"), the process which calls
> set_disable_master_thread() hangs if the con.owner already exited,
> because set_disable_master_thread() waits for cons_master_thread
> accepting the status change and reflecting the current status to
> master_thread_suspended. With this patch, set_disable_master_thread()
> is aborted if the owner process no longer exists to avoid this
> hang.
> 
> Addresses: https://cygwin.com/pipermail/cygwin/2026-September/260037.html
> Fixes: 733d5a953fa9 ("Cygwin: console: Ensure the master thread runs only when it is supposed to")
> Reported-by: Jay Libove Alzina <libove@felines.org>
> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> Reviewed-by:
> ---
> v2:
>   Fix the race
> 
>  winsup/cygwin/fhandler/console.cc | 15 ++++++++++++++-
>  1 file changed, 14 insertions(+), 1 deletion(-)
> 
> diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc
> index 5f78d0c62..4b1ba60ca 100644
> --- a/winsup/cygwin/fhandler/console.cc
> +++ b/winsup/cygwin/fhandler/console.cc
> @@ -5009,11 +5009,24 @@ fhandler_console::set_disable_master_thread (bool x, fhandler_console *cons)
>  	return;
>      }
>    const _minor_t unit = cons->get_minor ();
> +  if (con.owner == 0)
> +    /* The process that runs cons_master_thread no longer exists. */
> +    return;

This discards a request that a replacement worker must inherit.

A reachable retired state is `con.owner=0`, `disable_master_thread=false`,
`master_thread_suspended=true`: the worker yielded to a reader, then its
hosting process closed a console descriptor that was not its controlling
terminal, without requesting suspension. A surviving foreground Cygwin
process subsequently requests suspension before starting a native
application, but the initial guard returns without recording the true
request. A later Cygwin process with redirected stdin and console stdout
can adopt that console and start a replacement worker without resetting
the false request. It therefore runs and can consume flow-control
characters intended for the native reader. V2's later normalization cannot
help: the initial return bypasses it.

Before this patch, the true request was recorded, and the already-true
acknowledgement let the call finish immediately. Thus this is a regression
in a previously completing case, not simply a different outcome for an
already-hanging case. The defect comes from v1's guard and remains in v2.

The basic idea is: a missing worker lets us stop waiting, but not discard
the request its replacement needs. We need to preserve or normalize the
shared request consistently under input-state synchronization on both
no-owner exits, without removing the dead-owner escape.

Thanks,
Johannes

>    cons->acquire_input_mutex (mutex_timeout);
>    con.disable_master_thread = x;
>    cons->release_input_mutex ();
>    while (con.master_thread_suspended != x)
> -    Sleep (1);
> +    {
> +      Sleep (1);
> +      if (con.owner == 0)
> +	{
> +	  cons->acquire_input_mutex (mutex_timeout);
> +	  con.disable_master_thread = true;
> +	  con.master_thread_suspended = true;
> +	  cons->release_input_mutex ();
> +	  return;
> +	}
> +    }
>  }
>  
>  int
> -- 
> 2.51.0
> 
> 


More information about the Cygwin-patches mailing list