2023-03-01 14:10:23

by Jan Kara

[permalink] [raw]
Subject: [PATCH] ext4: Fix deadlock during directory rename

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: [email protected]
Fixes: 0813299c586b ("ext4: Fix possible corruption when moving a directory")
Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574
Signed-off-by: Jan Kara <[email protected]>
---
fs/ext4/namei.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 94608b7df7e8..426013b27816 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3813,9 +3813,19 @@ static int ext4_rename(struct mnt_idmap *idmap, 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, NULL);
- 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
@@ -3872,11 +3882,6 @@ static int ext4_rename(struct mnt_idmap *idmap, 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);
@@ -4013,12 +4018,15 @@ static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
} 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;
}

--
2.35.3



2023-03-08 04:33:50

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH] ext4: Fix deadlock during directory rename

On Wed, 1 Mar 2023 15:10:04 +0100, Jan Kara wrote:
> 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().
>
>
> [...]

Applied, thanks!

[1/1] ext4: Fix deadlock during directory rename
commit: 3c92792da8506a295afb6d032b4476e46f979725

Best regards,
--
Theodore Ts'o <[email protected]>