summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2023-03-01 17:10:04 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-03-17 10:50:29 +0300
commit2ef7f829214fa8f428d953b49557b89b2b02db66 (patch)
tree11c9744ef8452b340d6beb63ec06b834fdd38c79
parentdc7d4b1a6568652f51ee45b1a1e3531f1186a552 (diff)
downloadlinux-2ef7f829214fa8f428d953b49557b89b2b02db66.tar.xz
ext4: Fix deadlock during directory rename
[ Upstream commit 3c92792da8506a295afb6d032b4476e46f979725 ] As lockdep properly warns, we should not be locking i_rwsem while having transactions started as the proper lock ordering used by all directory handling operations is i_rwsem -> transaction start. Fix the lock ordering by moving the locking of the directory earlier in ext4_rename(). Reported-by: syzbot+9d16c39efb5fade84574@syzkaller.appspotmail.com Fixes: 0813299c586b ("ext4: Fix possible corruption when moving a directory") Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574 Signed-off-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20230301141004.15087-1-jack@suse.cz Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--fs/ext4/namei.c26
1 files changed, 17 insertions, 9 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index dc8f8a435a7e..800d631c920b 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3813,10 +3813,20 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
return retval;
}
+ /*
+ * We need to protect against old.inode directory getting converted
+ * from inline directory format into a normal one.
+ */
+ if (S_ISDIR(old.inode->i_mode))
+ inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
+
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
&old.inlined);
- if (IS_ERR(old.bh))
- return PTR_ERR(old.bh);
+ if (IS_ERR(old.bh)) {
+ retval = PTR_ERR(old.bh);
+ goto unlock_moved_dir;
+ }
+
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
@@ -3873,11 +3883,6 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
goto end_rename;
}
- /*
- * We need to protect against old.inode directory getting
- * converted from inline directory format into a normal one.
- */
- inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
retval = ext4_rename_dir_prepare(handle, &old);
if (retval) {
inode_unlock(old.inode);
@@ -4014,12 +4019,15 @@ end_rename:
} else {
ext4_journal_stop(handle);
}
- if (old.dir_bh)
- inode_unlock(old.inode);
release_bh:
brelse(old.dir_bh);
brelse(old.bh);
brelse(new.bh);
+
+unlock_moved_dir:
+ if (S_ISDIR(old.inode->i_mode))
+ inode_unlock(old.inode);
+
return retval;
}