Commit 50309845 authored by Jan Kara's avatar Jan Kara Committed by sanglipeng
Browse files

ext4: Fix deadlock during directory rename

stable inclusion
from stable-v5.10.175
commit b113f90204479f55a17295bedf0cc966a60c7a56
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I8711T

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=b113f90204479f55a17295bedf0cc966a60c7a56



--------------------------------

[ Upstream commit 3c92792d ]

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: default avatar <syzbot+9d16c39efb5fade84574@syzkaller.appspotmail.com>
Fixes: 0813299c ("ext4: Fix possible corruption when moving a directory")
Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574


Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20230301141004.15087-1-jack@suse.cz


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarsanglipeng <sanglipeng1@jd.com>
parent d2e413f5
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -3898,10 +3898,20 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
			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
@@ -3958,11 +3968,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
			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);
@@ -4092,12 +4097,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
	} 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;
}