Skip to content

Commit 3c92792

Browse files
jankaratytso
authored andcommitted
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: 0813299 ("ext4: Fix possible corruption when moving a directory") Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574 Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 7fc1f5c commit 3c92792

File tree

1 file changed

+17
-9
lines changed

1 file changed

+17
-9
lines changed

fs/ext4/namei.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,10 +3813,20 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
38133813
return retval;
38143814
}
38153815

3816+
/*
3817+
* We need to protect against old.inode directory getting converted
3818+
* from inline directory format into a normal one.
3819+
*/
3820+
if (S_ISDIR(old.inode->i_mode))
3821+
inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
3822+
38163823
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
38173824
&old.inlined);
3818-
if (IS_ERR(old.bh))
3819-
return PTR_ERR(old.bh);
3825+
if (IS_ERR(old.bh)) {
3826+
retval = PTR_ERR(old.bh);
3827+
goto unlock_moved_dir;
3828+
}
3829+
38203830
/*
38213831
* Check for inode number is _not_ due to possible IO errors.
38223832
* 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,
38733883
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
38743884
goto end_rename;
38753885
}
3876-
/*
3877-
* We need to protect against old.inode directory getting
3878-
* converted from inline directory format into a normal one.
3879-
*/
3880-
inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
38813886
retval = ext4_rename_dir_prepare(handle, &old);
38823887
if (retval) {
38833888
inode_unlock(old.inode);
@@ -4014,12 +4019,15 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
40144019
} else {
40154020
ext4_journal_stop(handle);
40164021
}
4017-
if (old.dir_bh)
4018-
inode_unlock(old.inode);
40194022
release_bh:
40204023
brelse(old.dir_bh);
40214024
brelse(old.bh);
40224025
brelse(new.bh);
4026+
4027+
unlock_moved_dir:
4028+
if (S_ISDIR(old.inode->i_mode))
4029+
inode_unlock(old.inode);
4030+
40234031
return retval;
40244032
}
40254033

0 commit comments

Comments
 (0)