[Devel,rh7] ovl: verify upper dentry in ovl_remove_and_whiteout()

Submitted by Maxim Patlasov on July 23, 2016, 1:50 a.m.

Details

Message ID 20160723015011.16498.89627.stgit@maxim-thinkpad
State New
Series "ovl: verify upper dentry in ovl_remove_and_whiteout()"
Headers show

Commit Message

Maxim Patlasov July 23, 2016, 1:50 a.m.
Backport from git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git:

commit cfc9fde0b07c3b44b570057c5f93dda59dca1c94
Author: Maxim Patlasov <mpatlasov@virtuozzo.com>
Date:   Thu Jul 21 18:24:26 2016 -0700

    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 11f3710417d0 ("ovl: verify upper dentry before unlink and rename").

    Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com>
    Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
    Cc: <stable@vger.kernel.org>

https://jira.sw.ru/browse/PSBM-47981
---
 fs/overlayfs/dir.c |   54 +++++++++++++++++++++++-----------------------------
 1 file changed, 24 insertions(+), 30 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 229b9e4..5402b9b 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -511,6 +511,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;
@@ -540,46 +541,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))
+	upper = lookup_one_len(dentry->d_name.name, upperdir,
+			       dentry->d_name.len);
+	err = PTR_ERR(upper);
+	if (IS_ERR(upper))
 		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;
+	err = -ESTALE;
+	if ((opaquedir && upper != opaquedir) ||
+	    (!opaquedir && ovl_dentry_upper(dentry) &&
+	     upper != ovl_dentry_upper(dentry))) {
+		goto out_dput_upper;
+	}
 
-		if (opaquedir)
-			upper = opaquedir;
-		err = -ESTALE;
-		if (upper->d_parent != upperdir)
-			goto kill_whiteout;
+	whiteout = ovl_whiteout(workdir, dentry);
+	err = PTR_ERR(whiteout);
+	if (IS_ERR(whiteout))
+		goto out_dput_upper;
 
-		if (is_dir)
-			flags |= RENAME_EXCHANGE;
+	if (d_is_dir(upper))
+		flags = RENAME_EXCHANGE;
 
-		err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
-		if (err)
-			goto kill_whiteout;
+	err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+	if (err)
+		goto kill_whiteout;
+	if (flags)
+		ovl_cleanup(wdir, upper);
 
-		if (is_dir)
-			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: