A lookup of ".." with SPEC_DOTDOT and LOOKUP releases the directory
only when it succeeds; its callers, checkpath and ancestors in
diskfs_rename_dir, release it themselves otherwise.  diskfs_lookup_hard
released it before diskfs_cached_lookup of the parent, so when that
failed the directory was unlocked and dereferenced twice.

Hold a reference across the lookup, and give the directory back locked
when it fails.
---
 ext2fs/dir.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/ext2fs/dir.c b/ext2fs/dir.c
index 09f5a7225..583232f36 100644
--- a/ext2fs/dir.c
+++ b/ext2fs/dir.c
@@ -316,10 +316,17 @@ diskfs_lookup_hard (struct node *dp, const char *name, 
lookup_flags_t l_flags,
 
       else if (type == LOOKUP)
        {
+         /* Hold DP until the lookup succeeds: on failure the caller
+            still has it, locked.  */
+         diskfs_nref (dp);
          diskfs_nput (dp);
          err = diskfs_cached_lookup (inum, &np);
          if (err)
-           goto out;
+           {
+             pthread_mutex_lock (&dp->lock);
+             goto out;
+           }
+         diskfs_nrele (dp);
        }
       else
        assert_backtrace (0);
-- 
2.56.0


Reply via email to