Commit 290da20e authored by Al Viro's avatar Al Viro
Browse files

do_move_mount(): split the checks in subtree-of-our-ns and entire-anon cases



... and fix the breakage in anon-to-anon case.  There are two cases
acceptable for do_move_mount() and mixing checks for those is making
things hard to follow.

One case is move of a subtree in caller's namespace.
        * source and destination must be in caller's namespace
	* source must be detachable from parent
Another is moving the entire anon namespace elsewhere
	* source must be the root of anon namespace
	* target must either in caller's namespace or in a suitable
	  anon namespace (see may_use_mount() for details).
	* target must not be in the same namespace as source.

It's really easier to follow if tests are *not* mixed together...

Reviewed-by: default avatarChristian Brauner <brauner@kernel.org>
Fixes: 3b5260d1 ("Don't propagate mounts into detached trees")
Reported-by: default avatarAllison Karlitskaya <lis@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 4954346d
Loading
Loading
Loading
Loading
+25 −21
Original line number Diff line number Diff line
@@ -3656,36 +3656,40 @@ static int do_move_mount(struct path *old_path,
	ns = old->mnt_ns;

	err = -EINVAL;
	if (!may_use_mount(p))
		goto out;

	/* The thing moved must be mounted... */
	if (!is_mounted(&old->mnt))
		goto out;

	/* ... and either ours or the root of anon namespace */
	if (!(attached ? check_mnt(old) : is_anon_ns(ns)))
	if (check_mnt(old)) {
		/* if the source is in our namespace... */
		/* ... it should be detachable from parent */
		if (!mnt_has_parent(old) || IS_MNT_LOCKED(old))
			goto out;

	if (is_anon_ns(ns) && ns == p->mnt_ns) {
		/* ... and the target should be in our namespace */
		if (!check_mnt(p))
			goto out;
	} else {
		/*
		 * Ending up with two files referring to the root of the
		 * same anonymous mount namespace would cause an error
		 * as this would mean trying to move the same mount
		 * twice into the mount tree which would be rejected
		 * later. But be explicit about it right here.
		 * otherwise the source must be the root of some anon namespace.
		 * AV: check for mount being root of an anon namespace is worth
		 * an inlined predicate...
		 */
		if (!is_anon_ns(ns) || mnt_has_parent(old))
			goto out;
	} else if (is_anon_ns(p->mnt_ns)) {
		/*
		 * Don't allow moving an attached mount tree to an
		 * anonymous mount tree.
		 * Bail out early if the target is within the same namespace -
		 * subsequent checks would've rejected that, but they lose
		 * some corner cases if we check it early.
		 */
		if (ns == p->mnt_ns)
			goto out;
	}

	if (old->mnt.mnt_flags & MNT_LOCKED)
		/*
		 * Target should be either in our namespace or in an acceptable
		 * anon namespace, sensu check_anonymous_mnt().
		 */
		if (!may_use_mount(p))
			goto out;
	}

	if (!path_mounted(old_path))
		goto out;