Hi Takashi,

On Nov 24 12:30, Takashi Yano wrote:
> Previously, variable i_all_lf was a member of inode_t even though
> it is used only temporarily as commented in flock.cc.
> This easily leads to bugs like those that occurred in flock.cc,
> such as:
> 
>   lf_setlock()            lf_clearlock()
> 
>        |                       .
>    i_all_lf = tp.w_get()       .
>        |                       .
>        +---------------------->+
>                                |
>                            i_all_lf = tp.wget()
>                                |
>                            do something
>                                |
>                            (release i_all_lf implicitly)
>                                |
>        +<----------------------+
>        |
>    accessing i_all_lf (may destroy tmp_pathbuf area)
>        |
> 
> With this patch, to fix and prevent the bugs, move i_all_lf from a
> member of inode_t to a local (auto) variable in each function that
> uses it.
> 
> Addresses: https://cygwin.com/pipermail/cygwin/2025-October/258914.html
> Fixes: a998dd705576 ("* flock.cc: Implement all advisory file locking here.")

Thanks for tracking this down, this is great.

However, this problem hasn't been introduced by a998dd705576, but rather
by ae181b0ff122 ("Cygwin: lockf: Make lockf() return ENOLCK when too
many locks") last year.

The reason is that, up to this point, lf_clearlock() did not have to
access inode_t::i_all_lf at all.  Rather, initialization and destruction
were contained within lf_setlock() and lf_getlock(). But lf_clearlock()
isn't called from fhandler_base::lock() alone.

Now (i.e. after ae181b0ff122) that lf_clearlock() requires the buffer as
well, all basic lock functions called from fhandler_base::lock() require
the buffer.

So I wonder...

Wouldn't this simple patch just moving the tmp_pathbuf up into
fhandler_base::lock() fix the problem just as well, plus avoiding
multiple w_get() calls?

diff --git a/winsup/cygwin/flock.cc b/winsup/cygwin/flock.cc
index e03caba27a8e..3f43c4fe352f 100644
--- a/winsup/cygwin/flock.cc
+++ b/winsup/cygwin/flock.cc
@@ -945,6 +945,7 @@ fhandler_base::lock (int a_op, struct flock *fl)
 {
   off_t start, end, oadd;
   int error = 0;
+  tmp_pathbuf tp;
 
   short a_flags = fl->l_type & (F_OFD | F_POSIX | F_FLOCK);
   short type = fl->l_type & (F_RDLCK | F_WRLCK | F_UNLCK);
@@ -1149,6 +1150,8 @@ restart:  /* Entry point after a restartable signal came 
in. */
       return -1;
     }
 
+  node->i_all_lf = (lockf_t *) tp.w_get ();
+
   switch (a_op)
     {
     case F_SETLK:
@@ -1218,7 +1221,6 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t 
**clean, HANDLE fhdl)
   lockf_t **head = lock->lf_head;
   lockf_t **prev, *overlap;
   int ovcase, priority, old_prio, needtolink;
-  tmp_pathbuf tp;
 
   /*
    * Set the priority
@@ -1230,7 +1232,6 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t 
**clean, HANDLE fhdl)
    * Scan lock list for this file looking for locks that would block us.
    */
   /* Create temporary space for the all locks list. */
-  node->i_all_lf = (lockf_t *) (void *) tp.w_get ();
   while ((block = lf_getblock(lock, node)))
     {
       HANDLE obj = block->lf_obj;
@@ -1543,8 +1544,6 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE 
fhdl)
     return 0;
 
   inode_t *node = lf->lf_inode;
-  tmp_pathbuf tp;
-  node->i_all_lf = (lockf_t *) tp.w_get ();
   node->get_all_locks_list (); /* Update lock count */
   uint32_t lock_cnt = node->get_lock_count ();
   bool first_loop = true;
@@ -1631,10 +1630,8 @@ static int
 lf_getlock (lockf_t *lock, inode_t *node, struct flock *fl)
 {
   lockf_t *block;
-  tmp_pathbuf tp;
 
   /* Create temporary space for the all locks list. */
-  node->i_all_lf = (lockf_t *) (void * ) tp.w_get ();
   if ((block = lf_getblock (lock, node)))
     {
       if (block->lf_obj)

Reply via email to