Commit cfc9fde0 authored by Maxim Patlasov's avatar Maxim Patlasov Committed by Miklos Szeredi
Browse files

ovl: verify upper dentry in ovl_remove_and_whiteout()



The upper dentry may become stale before we call ovl_lock_rename_workdir.
For example, someone could (mistakenly or maliciously) manually unlink(2)
it directly from upperdir.

To ensure it is not stale, let's lookup it after ovl_lock_rename_workdir
and and check if it matches the upper dentry.

Essentially, it is the same problem and similar solution as in
commit 11f37104 ("ovl: verify upper dentry before unlink and rename").

Signed-off-by: default avatarMaxim Patlasov <mpatlasov@virtuozzo.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Cc: <stable@vger.kernel.org>
parent 07a2daab
Loading
Loading
Loading
Loading
+24 −30
Original line number Diff line number Diff line
@@ -505,6 +505,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
	struct dentry *upper;
	struct dentry *opaquedir = NULL;
	int err;
	int flags = 0;

	if (WARN_ON(!workdir))
		return -EROFS;
@@ -534,46 +535,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
	if (err)
		goto out_dput;

	whiteout = ovl_whiteout(workdir, dentry);
	err = PTR_ERR(whiteout);
	if (IS_ERR(whiteout))
		goto out_unlock;

	upper = ovl_dentry_upper(dentry);
	if (!upper) {
	upper = lookup_one_len(dentry->d_name.name, upperdir,
			       dentry->d_name.len);
	err = PTR_ERR(upper);
	if (IS_ERR(upper))
			goto kill_whiteout;

		err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
		dput(upper);
		if (err)
			goto kill_whiteout;
	} else {
		int flags = 0;
		goto out_unlock;

		if (opaquedir)
			upper = opaquedir;
	err = -ESTALE;
		if (upper->d_parent != upperdir)
			goto kill_whiteout;
	if ((opaquedir && upper != opaquedir) ||
	    (!opaquedir && ovl_dentry_upper(dentry) &&
	     upper != ovl_dentry_upper(dentry))) {
		goto out_dput_upper;
	}

		if (is_dir)
			flags |= RENAME_EXCHANGE;
	whiteout = ovl_whiteout(workdir, dentry);
	err = PTR_ERR(whiteout);
	if (IS_ERR(whiteout))
		goto out_dput_upper;

	if (d_is_dir(upper))
		flags = RENAME_EXCHANGE;

	err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
	if (err)
		goto kill_whiteout;

		if (is_dir)
	if (flags)
		ovl_cleanup(wdir, upper);
	}

	ovl_dentry_version_inc(dentry->d_parent);
out_d_drop:
	d_drop(dentry);
	dput(whiteout);
out_dput_upper:
	dput(upper);
out_unlock:
	unlock_rename(workdir, upperdir);
out_dput: