Commit 3c92792d authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

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: syzbot+9d16c39efb5fade84574@syzkaller.appspotmail.com
Fixes: 0813299c ("ext4: Fix possible corruption when moving a directory")
Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20230301141004.15087-1-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 7fc1f5c2
...@@ -3813,10 +3813,20 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir, ...@@ -3813,10 +3813,20 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
return retval; 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.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
&old.inlined); &old.inlined);
if (IS_ERR(old.bh)) if (IS_ERR(old.bh)) {
return PTR_ERR(old.bh); retval = PTR_ERR(old.bh);
goto unlock_moved_dir;
}
/* /*
* Check for inode number is _not_ due to possible IO errors. * Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process * 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, ...@@ -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)) if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
goto end_rename; 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); retval = ext4_rename_dir_prepare(handle, &old);
if (retval) { if (retval) {
inode_unlock(old.inode); inode_unlock(old.inode);
...@@ -4014,12 +4019,15 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir, ...@@ -4014,12 +4019,15 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
} else { } else {
ext4_journal_stop(handle); ext4_journal_stop(handle);
} }
if (old.dir_bh)
inode_unlock(old.inode);
release_bh: release_bh:
brelse(old.dir_bh); brelse(old.dir_bh);
brelse(old.bh); brelse(old.bh);
brelse(new.bh); brelse(new.bh);
unlock_moved_dir:
if (S_ISDIR(old.inode->i_mode))
inode_unlock(old.inode);
return retval; return retval;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment