linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Al Viro <viro@ZenIV.linux.org.uk>
To: linux-fsdevel@vger.kernel.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
	linux-kernel@vger.kernel.org, Aleksa Sarai <cyphar@cyphar.com>,
	David Howells <dhowells@redhat.com>,
	Eric Biederman <ebiederm@xmission.com>,
	Christian Brauner <christian.brauner@ubuntu.com>,
	Al Viro <viro@zeniv.linux.org.uk>
Subject: [PATCH 02/17] fix automount/automount race properly
Date: Sun, 19 Jan 2020 03:17:14 +0000	[thread overview]
Message-ID: <20200119031738.2681033-2-viro@ZenIV.linux.org.uk> (raw)
In-Reply-To: <20200119031738.2681033-1-viro@ZenIV.linux.org.uk>

From: Al Viro <viro@zeniv.linux.org.uk>

Protection against automount/automount races (two threads hitting the same
referral point at the same time) is based upon do_add_mount() prevention of
identical overmounts - trying to overmount the root of mounted tree with
the same tree fails with -EBUSY.  It's unreliable (the other thread might've
mounted something on top of the automount it has triggered) *and* causes
no end of headache for follow_automount() and its caller, since
finish_automount() behaves like do_new_mount() - if the mountpoint to be is
overmounted, it mounts on top what's overmounting it.  It's not only wrong
(we want to go into what's overmounting the automount point and quietly
discard what we planned to mount there), it introduces the possibility of
original parent mount getting dropped.  That's what 8aef18845266 (VFS: Fix
vfsmount overput on simultaneous automount) deals with, but it can't do
anything about the reliability of conflict detection - if something had
been overmounted the other thread's automount (e.g. that other thread
having stepped into automount in mount(2)), we don't get that -EBUSY and
the result is
	 referral point under automounted NFS under explicit overmount
under another copy of automounted NFS

What we need is finish_automount() *NOT* digging into overmounts - if it
finds one, it should just quietly discard the thing it was asked to mount.
And don't bother with actually crossing into the results of finish_automount() -
the same loop that calls follow_automount() will do that just fine on the
next iteration.

IOW, instead of calling lock_mount() have finish_automount() do it manually,
_without_ the "move into overmount and retry" part.  And leave crossing into
the results to the caller of follow_automount(), which simplifies it a lot.

Moral: if you end up with a lot of glue working around the calling conventions
of something, perhaps these calling conventions are simply wrong...

Fixes: 8aef18845266 (VFS: Fix vfsmount overput on simultaneous automount)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/namei.c     | 29 ++++-------------------------
 fs/namespace.c | 41 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index d2720dc71d0e..bd036dfdb0d9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1133,11 +1133,9 @@ EXPORT_SYMBOL(follow_up);
  * - return -EISDIR to tell follow_managed() to stop and return the path we
  *   were called with.
  */
-static int follow_automount(struct path *path, struct nameidata *nd,
-			    bool *need_mntput)
+static int follow_automount(struct path *path, struct nameidata *nd)
 {
 	struct vfsmount *mnt;
-	int err;
 
 	if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
 		return -EREMOTE;
@@ -1178,29 +1176,10 @@ static int follow_automount(struct path *path, struct nameidata *nd,
 		return PTR_ERR(mnt);
 	}
 
-	if (!mnt) /* mount collision */
-		return 0;
-
-	if (!*need_mntput) {
-		/* lock_mount() may release path->mnt on error */
-		mntget(path->mnt);
-		*need_mntput = true;
-	}
-	err = finish_automount(mnt, path);
-
-	switch (err) {
-	case -EBUSY:
-		/* Someone else made a mount here whilst we were busy */
+	if (!mnt)
 		return 0;
-	case 0:
-		path_put(path);
-		path->mnt = mnt;
-		path->dentry = dget(mnt->mnt_root);
-		return 0;
-	default:
-		return err;
-	}
 
+	return finish_automount(mnt, path);
 }
 
 /*
@@ -1258,7 +1237,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
 
 		/* Handle an automount point */
 		if (flags & DCACHE_NEED_AUTOMOUNT) {
-			ret = follow_automount(path, nd, &need_mntput);
+			ret = follow_automount(path, nd);
 			if (ret < 0)
 				break;
 			continue;
diff --git a/fs/namespace.c b/fs/namespace.c
index 5f0a80f17651..f1817eb5f87d 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2823,6 +2823,7 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
 
 int finish_automount(struct vfsmount *m, struct path *path)
 {
+	struct dentry *dentry = path->dentry;
 	struct mount *mnt = real_mount(m);
 	struct mountpoint *mp;
 	int err;
@@ -2832,21 +2833,47 @@ int finish_automount(struct vfsmount *m, struct path *path)
 	BUG_ON(mnt_get_count(mnt) < 2);
 
 	if (m->mnt_sb == path->mnt->mnt_sb &&
-	    m->mnt_root == path->dentry) {
+	    m->mnt_root == dentry) {
 		err = -ELOOP;
-		goto fail;
+		goto discard;
 	}
 
-	mp = lock_mount(path);
+	/*
+	 * we don't want to use lock_mount() - in this case finding something
+	 * that overmounts our mountpoint to be means "quitely drop what we've
+	 * got", not "try to mount it on top".
+	 */
+	inode_lock(dentry->d_inode);
+	if (unlikely(cant_mount(dentry))) {
+		err = -ENOENT;
+		goto discard1;
+	}
+	namespace_lock();
+	rcu_read_lock();
+	if (unlikely(__lookup_mnt(path->mnt, dentry))) {
+		rcu_read_unlock();
+		err = 0;
+		goto discard2;
+	}
+	rcu_read_unlock();
+	mp = get_mountpoint(dentry);
 	if (IS_ERR(mp)) {
 		err = PTR_ERR(mp);
-		goto fail;
+		goto discard2;
 	}
+
 	err = do_add_mount(mnt, mp, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
 	unlock_mount(mp);
-	if (!err)
-		return 0;
-fail:
+	if (unlikely(err))
+		goto discard;
+	mntput(m);
+	return 0;
+
+discard2:
+	namespace_unlock();
+discard1:
+	inode_unlock(dentry->d_inode);
+discard:
 	/* remove m from any expiration list it may be on */
 	if (!list_empty(&mnt->mnt_expire)) {
 		namespace_lock();
-- 
2.20.1


  reply	other threads:[~2020-01-19  3:18 UTC|newest]

Thread overview: 92+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-30  5:20 [PATCH RFC 0/1] mount: universally disallow mounting over symlinks Aleksa Sarai
2019-12-30  5:20 ` [PATCH RFC 1/1] " Aleksa Sarai
2019-12-30  7:34   ` Linus Torvalds
2019-12-30  8:28     ` Aleksa Sarai
2020-01-08  4:39       ` Andy Lutomirski
2019-12-30  5:44 ` [PATCH RFC 0/1] " Al Viro
2019-12-30  5:49   ` Aleksa Sarai
2019-12-30  7:29     ` Aleksa Sarai
2019-12-30  7:53       ` Linus Torvalds
2019-12-30  8:32         ` Aleksa Sarai
2020-01-02  8:58           ` David Laight
2020-01-02  9:09             ` Aleksa Sarai
2020-01-01  0:43       ` Al Viro
2020-01-01  0:54         ` Al Viro
2020-01-01  3:08           ` Al Viro
2020-01-01 14:44             ` Aleksa Sarai
2020-01-01 23:40               ` Al Viro
2020-01-02  3:59                 ` Aleksa Sarai
2020-01-03  1:49                   ` Al Viro
2020-01-04  4:46                     ` Ian Kent
2020-01-08  3:13                     ` Al Viro
2020-01-08  3:54                       ` Linus Torvalds
2020-01-08 21:34                         ` Al Viro
2020-01-10  0:08                           ` Linus Torvalds
2020-01-10  4:15                             ` Al Viro
2020-01-10  5:03                               ` Linus Torvalds
2020-01-10  6:20                               ` Ian Kent
2020-01-12 21:33                                 ` Al Viro
2020-01-13  2:59                                   ` Ian Kent
2020-01-14  0:25                                     ` Ian Kent
2020-01-14  4:39                                       ` Al Viro
2020-01-14  5:01                                         ` Ian Kent
2020-01-14  5:59                                           ` Ian Kent
2020-01-10 21:07                         ` Aleksa Sarai
2020-01-14  4:57                           ` Al Viro
2020-01-14  5:12                             ` Al Viro
2020-01-14 20:01                             ` Aleksa Sarai
2020-01-15 14:25                               ` Al Viro
2020-01-15 14:29                                 ` Aleksa Sarai
2020-01-15 14:34                                   ` Aleksa Sarai
2020-01-15 14:48                                     ` Al Viro
2020-01-18 12:07                                       ` [PATCH v3 0/2] openat2: minor uapi cleanups Aleksa Sarai
2020-01-18 12:07                                         ` [PATCH v3 1/2] open: introduce openat2(2) syscall Aleksa Sarai
2020-01-18 12:08                                         ` [PATCH v3 2/2] selftests: add openat2(2) selftests Aleksa Sarai
2020-01-18 15:28                                         ` [PATCH v3 0/2] openat2: minor uapi cleanups Al Viro
2020-01-18 18:09                                           ` Al Viro
2020-01-18 23:03                                             ` Aleksa Sarai
2020-01-19  1:12                                               ` Al Viro
2020-01-15 13:57                             ` [PATCH RFC 0/1] mount: universally disallow mounting over symlinks Aleksa Sarai
2020-01-19  3:14                               ` [RFC][PATCHSET][CFT] pathwalk cleanups and fixes Al Viro
2020-01-19  3:17                                 ` [PATCH 01/17] do_add_mount(): lift lock_mount/unlock_mount into callers Al Viro
2020-01-19  3:17                                   ` Al Viro [this message]
2020-01-30 14:34                                     ` [PATCH 02/17] fix automount/automount race properly Christian Brauner
2020-01-19  3:17                                   ` [PATCH 03/17] follow_automount(): get rid of dead^Wstillborn code Al Viro
2020-01-30 14:38                                     ` Christian Brauner
2020-01-19  3:17                                   ` [PATCH 04/17] follow_automount() doesn't need the entire nameidata Al Viro
2020-01-30 14:45                                     ` Christian Brauner
2020-01-30 15:38                                       ` Al Viro
2020-01-30 15:55                                         ` Al Viro
2020-01-19  3:17                                   ` [PATCH 05/17] make build_open_flags() treat O_CREAT | O_EXCL as implying O_NOFOLLOW Al Viro
2020-01-19  3:17                                   ` [PATCH 06/17] handle_mounts(): start building a sane wrapper for follow_managed() Al Viro
2020-01-19  3:17                                   ` [PATCH 07/17] atomic_open(): saner calling conventions (return dentry on success) Al Viro
2020-01-19  3:17                                   ` [PATCH 08/17] lookup_open(): " Al Viro
2020-01-19  3:17                                   ` [PATCH 09/17] do_last(): collapse the call of path_to_nameidata() Al Viro
2020-01-19  3:17                                   ` [PATCH 10/17] handle_mounts(): pass dentry in, turn path into a pure out argument Al Viro
2020-01-19  3:17                                   ` [PATCH 11/17] lookup_fast(): consolidate the RCU success case Al Viro
2020-01-19  3:17                                   ` [PATCH 12/17] teach handle_mounts() to handle RCU mode Al Viro
2020-01-19  3:17                                   ` [PATCH 13/17] lookup_fast(): take mount traversal into callers Al Viro
2020-01-19  3:17                                   ` [PATCH 14/17] new step_into() flag: WALK_NOFOLLOW Al Viro
2020-01-19  3:17                                   ` [PATCH 15/17] fold handle_mounts() into step_into() Al Viro
2020-01-19  3:17                                   ` [PATCH 16/17] LOOKUP_MOUNTPOINT: fold path_mountpointat() into path_lookupat() Al Viro
2020-01-19  3:17                                   ` [PATCH 17/17] expand the only remaining call of path_lookup_conditional() Al Viro
2020-01-19  3:17                                   ` [PATCH 1/9] merging pick_link() with get_link(), part 1 Al Viro
2020-01-19  3:17                                   ` [PATCH 2/9] merging pick_link() with get_link(), part 2 Al Viro
2020-01-19  3:17                                   ` [PATCH 3/9] merging pick_link() with get_link(), part 3 Al Viro
2020-01-19  3:17                                   ` [PATCH 4/9] merging pick_link() with get_link(), part 4 Al Viro
2020-01-19  3:17                                   ` [PATCH 5/9] merging pick_link() with get_link(), part 5 Al Viro
2020-01-19  3:17                                   ` [PATCH 6/9] merging pick_link() with get_link(), part 6 Al Viro
2020-01-19  3:17                                   ` [PATCH 7/9] finally fold get_link() into pick_link() Al Viro
2020-01-19  3:17                                   ` [PATCH 8/9] massage __follow_mount_rcu() a bit Al Viro
2020-01-19  3:17                                   ` [PATCH 9/9] new helper: traverse_mounts() Al Viro
2020-01-30 14:13                                   ` [PATCH 01/17] do_add_mount(): lift lock_mount/unlock_mount into callers Christian Brauner
2020-01-19 14:33                                 ` [RFC][PATCHSET][CFT] pathwalk cleanups and fixes Ian Kent
2020-01-10 23:19                     ` [PATCH RFC 0/1] mount: universally disallow mounting over symlinks Al Viro
2020-01-13  1:48                       ` Ian Kent
2020-01-13  3:54                         ` Al Viro
2020-01-13  6:00                           ` Ian Kent
2020-01-13  6:03                             ` Ian Kent
2020-01-13 13:30                               ` Al Viro
2020-01-14  7:25                                 ` Ian Kent
2020-01-14 12:17                                   ` Ian Kent
2020-01-04  5:52               ` Andy Lutomirski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200119031738.2681033-2-viro@ZenIV.linux.org.uk \
    --to=viro@zeniv.linux.org.uk \
    --cc=christian.brauner@ubuntu.com \
    --cc=cyphar@cyphar.com \
    --cc=dhowells@redhat.com \
    --cc=ebiederm@xmission.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).