Re: [PATCH v2] Cygwin: open: Unlock fdtab before open_with_arch()

Mark Geisert <[email protected]> Thu, 16 Jul 2026 23:36:02 -0700 (PDT)
Newsgroups gmane.os.cygwin.patches
Message-ID <[email protected]>
Hi Takashi,
I'm offline for a while due to a system boot malfunction and subsequent 
rebuilding from backups so I cannot test patches as I normally would do. 
I do have some comments below...

On Fri, 17 Jul 2026, Takashi Yano wrote:
> Since the commit 31bf91f867c5, opening fifo causes a deadlock. This
> is because, open_with_arch() for fifo can be blocked until the other
> side of the fifo is opened. The commit 31bf91f867c5 moves the creating
> cygheap_fdnew before open_with_arch() to address the issue:
> https://cygwin.com/pipermail/cygwin/2026-May/259664.html
> However, cygheap_fdnew locks fdtab, so open() for the other side of
> fifo cannot create cygheap_fdnew until fdtab is unlocked. This is
> the cause of the deadlock.

Thank you for diagnosing the problem and coming up with a fix!

> With this patch, fdtab is unlocked before open_with_arch(), but marked
> as used using tentative fhandler. The summary of open() is as follows.
> 1) Lock fdtab.
> 2) Create new fd.
> 3) Mark fd as used using tentative fhandler.
> 4) Unlock fdtab.
> 5) Call open_with_arch().
> 6) Set final fhandler to fd.
>
> The important point is that create fd before open_with_arch() to
> address https://cygwin.com/pipermail/cygwin/2026-May/259664.html,
> but unlock fdtab before open_with_arch() to address
> https://cygwin.com/pipermail/cygwin/2026-July/259884.html.
>
> Fixes: 31bf91f867c5 ("Cygwin: Ensure unused fd available for open()")
> Addresses: https://cygwin.com/pipermail/cygwin/2026-July/259884.html
> Reported-by: kikairoya <[email protected]>
> Signed-off-by: Takashi Yano <[email protected]>
> Reviewed-by:
> ---
> v2: Add lock/unlock when modifying the fdtab, just to be safe.
>
> winsup/cygwin/syscalls.cc | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
> index 2bea79768..e3ba8c65c 100644
> --- a/winsup/cygwin/syscalls.cc
> +++ b/winsup/cygwin/syscalls.cc
> @@ -1451,6 +1451,7 @@ extern "C" int
> open (const char *unix_path, int flags, ...)
> {
>   int res = -1;
> +  int fd = -1;
>   va_list ap;
>   mode_t mode = 0;
>   fhandler_base *fh = NULL;
> @@ -1550,9 +1551,12 @@ open (const char *unix_path, int flags, ...)
>       /* Reserve an fdtable entry here, before calling open_with_arch() below.
>          Otherwise there's a tiny chance of hitting OPEN_MAX further on which
>          could create a new file without any way for Cygwin to refer to it. */
> -      cygheap_fdnew fd;
> +      cygheap->fdtab.lock();
> +      fd = cygheap->fdtab.find_unused_handle ();
>       if (fd < 0)
> -        __leave;		/* errno already set */
> +	__leave;		/* errno already set */

Not sure about the above two lines.. did one of us use TABs and the other 
did not?  A minor thing.

> +      cygheap->fdtab[fd] = fh; /* tentative setting to mark as used */

When I was looking into this area of code I couldn't determine if 'fh' was 
non-null in every code path.  That's why I had proposed a distinctive 
value to use (-1 IIRC).  Would be great if you know for sure it's safe.

> +      cygheap->fdtab.unlock();
>
>       if (fh->dev () == FH_PROCESSFD && fh->pc.follow_fd_symlink ())
> 	{
> @@ -1580,13 +1584,23 @@ open (const char *unix_path, int flags, ...)
> 	try_to_bin (fh->pc, fh->get_handle (), DELETE,
> 		    FILE_OPEN_FOR_BACKUP_INTENT);
>
> -      fd = fh;
> +      cygheap->fdtab.lock ();
> +      cygheap->fdtab[fd] = fh;
> +      fh->inc_refcnt ();
> +      cygheap->fdtab.unlock ();
> +
>       if (fd <= 2)
> 	set_std_handle (fd);
>       res = fd;
>     }
>   __except (EFAULT) {}
>   __endtry
> +    if (res < 0 && fd >= 0)
> +      {
> +	cygheap->fdtab.lock ();
> +	cygheap->fdtab[fd] = NULL; /* Mark as unused */
> +	cygheap->fdtab.unlock ();

I had wondered about using InterlockedExchange() but your code is more 
explicit, so I go with you on this.

> +      }
>   if (res < 0 && fh)
>     delete fh;
>   syscall_printf ("%R = open(%s, %y)", res, unix_path, flags);
> -- 
> 2.51.0
>

Thanks again Takashi for diving in so quickly on this report.
Regards,

..mark