diskfs_S_dir_rename unlocks FNP before it calls diskfs_rename_dir, which
locks it only with the lookup of FROMNAME.  The exits before that, when
the target lookup fails or the target exists with EXCL, jumped to OUT,
which unlocks FNP anyway.  Nothing shows when FNP is free, but a thread
holding it, such as a lookup in the directory being moved, loses its
lock.

Clear FNP before those exits, as the exits after the lookup do.

How to reproduce, as a user who cannot write /usr:

  $ mkdir ~/d && mv ~/d /usr/

The target lookup fails with EACCES, and OUT unlocks FNP, which this
thread does not hold.
---
 libdiskfs/dir-renamed.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c
index ed468b539..17efebb04 100644
--- a/libdiskfs/dir-renamed.c
+++ b/libdiskfs/dir-renamed.c
@@ -99,11 +99,16 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, 
const char *fromname,
   ds = buf;
   err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
   assert_backtrace (err != EAGAIN);    /* <-> assert_backtrace (TONAME != 
"..") */
+  /* FNP is not locked yet: OUT must not unlock it.  */
   if (err && err != ENOENT)
-    goto out;
+    {
+      fnp = NULL;
+      goto out;
+    }
   if (tnp && excl)
     {
       err = EEXIST;
+      fnp = NULL;
       goto out;
     }
 
-- 
2.56.0


Reply via email to