linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Aleksa Sarai <cyphar@cyphar.com>
To: Al Viro <viro@zeniv.linux.org.uk>,
	David Howells <dhowells@redhat.com>,
	Eric Biederman <ebiederm@xmission.com>,
	Linus Torvalds <torvalds@linux-foundation.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>,
	stable@vger.kernel.org,
	Christian Brauner <christian.brauner@ubuntu.com>,
	Serge Hallyn <serge@hallyn.com>,
	dev@opencontainers.org, containers@lists.linux-foundation.org,
	linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH RFC 1/1] mount: universally disallow mounting over symlinks
Date: Mon, 30 Dec 2019 16:20:36 +1100	[thread overview]
Message-ID: <20191230052036.8765-2-cyphar@cyphar.com> (raw)
In-Reply-To: <20191230052036.8765-1-cyphar@cyphar.com>

An undocumented feature of the mount interface was that it was possible
to mount over a symlink (even with the old mount API) by mounting over
/proc/self/fd/$n -- where the corresponding file descrpitor was opened
with (O_PATH|O_NOFOLLOW). This didn't work with traditional "new" mounts
(for a variety of reasons), but MS_BIND worked without issue. With the
new mount API it was even easier.

From userspace's perspective, this capability is only really useful as
an attack vector. Until the introduction of openat2(RESOLVE_NO_XDEV),
there was no trivial way to detect if a bind-mount was present. In the
container runtime context (in a similar vein to CVE-2019-19921), this
could result in a privileged process being unable to detect that a
configuration resulted in magic-link usage operating on the wrong
magic-links. Additionally, the API to use this feature was incredibly
strange -- in order to umount, you would have go through
/proc/self/fd/$n again (umounting the path would result in the
*underlying* symlink being followed).

Which brings us to the issues on the kernel side. When umounting a mount
on top of a symlink, several oopses (both NULL and garbage kernel
address dereferences) and deadlocks could be triggered incredibly
trivially. Note that because this works in user namespaces, an
unprivileged user could trigger these oopses incredibly trivially. While
these bugs could be fixed separately, it seems much cleaner to disable a
"feature" which clearly was not intentional (and is not used --
otherwise we would've seen bug reports about it breaking on umount).

Note that because the linux-utils mount(1) helper will expand paths
containing symlinks in user-space, only users which used the mount(2)
syscall directly could possibly have seen this behaviour.

Cc: stable@vger.kernel.org # pre-git
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: David Howells <dhowells@redhat.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
---
 fs/namespace.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index be601d3a8008..01a62bce105f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2172,8 +2172,12 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
 	if (mnt->mnt.mnt_sb->s_flags & SB_NOUSER)
 		return -EINVAL;
 
+	if (d_is_symlink(mp->m_dentry) ||
+	    d_is_symlink(mnt->mnt.mnt_root))
+		return -EINVAL;
+
 	if (d_is_dir(mp->m_dentry) !=
-	      d_is_dir(mnt->mnt.mnt_root))
+	    d_is_dir(mnt->mnt.mnt_root))
 		return -ENOTDIR;
 
 	return attach_recursive_mnt(mnt, p, mp, false);
@@ -2251,6 +2255,9 @@ static struct mount *__do_loopback(struct path *old_path, int recurse)
 	if (IS_MNT_UNBINDABLE(old))
 		return mnt;
 
+	if (d_is_symlink(old_path->dentry))
+		return mnt;
+
 	if (!check_mnt(old) && old_path->dentry->d_op != &ns_dentry_operations)
 		return mnt;
 
@@ -2635,6 +2642,10 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
 	if (old_path->dentry != old_path->mnt->mnt_root)
 		goto out;
 
+	if (d_is_symlink(new_path->dentry) ||
+	    d_is_symlink(old_path->dentry))
+		goto out;
+
 	if (d_is_dir(new_path->dentry) !=
 	    d_is_dir(old_path->dentry))
 		goto out;
@@ -2726,10 +2737,6 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
 	    path->mnt->mnt_root == path->dentry)
 		goto unlock;
 
-	err = -EINVAL;
-	if (d_is_symlink(newmnt->mnt.mnt_root))
-		goto unlock;
-
 	newmnt->mnt.mnt_flags = mnt_flags;
 	err = graft_tree(newmnt, parent, mp);
 
-- 
2.24.1


  reply	other threads:[~2019-12-30  5:21 UTC|newest]

Thread overview: 91+ 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 ` Aleksa Sarai [this message]
2019-12-30  7:34   ` [PATCH RFC 1/1] " 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
     [not found]     ` <20191230072959.62kcojxpthhdwmfa@yavin.dot.cyphar.com>
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                                   ` [PATCH 02/17] fix automount/automount race properly Al Viro
2020-01-30 14:34                                     ` 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=20191230052036.8765-2-cyphar@cyphar.com \
    --to=cyphar@cyphar.com \
    --cc=christian.brauner@ubuntu.com \
    --cc=containers@lists.linux-foundation.org \
    --cc=dev@opencontainers.org \
    --cc=dhowells@redhat.com \
    --cc=ebiederm@xmission.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=serge@hallyn.com \
    --cc=stable@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=viro@zeniv.linux.org.uk \
    /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).