Commit b19a97d5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull mount fixes from Al Viro:
 "Fixes for several recent mount-related regressions"

* tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  change_mnt_propagation(): calculate propagation source only if we'll need it
  use uniform permission checks for all mount propagation changes
  propagate_umount(): only surviving overmounts should be reparented
  fix the softlockups in attach_recursive_mnt()
parents 7cca555b fb924b7b
Loading
Loading
Loading
Loading
+22 −19
Original line number Diff line number Diff line
@@ -1197,9 +1197,6 @@ static void commit_tree(struct mount *mnt)

	if (!mnt_ns_attached(mnt)) {
		for (struct mount *m = mnt; m; m = next_mnt(m, mnt))
			if (unlikely(mnt_ns_attached(m)))
				m = skip_mnt_tree(m);
			else
			mnt_add_to_ns(n, m);
		n->nr_mounts += n->pending_mounts;
		n->pending_mounts = 0;
@@ -2704,6 +2701,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
			lock_mnt_tree(child);
		q = __lookup_mnt(&child->mnt_parent->mnt,
				 child->mnt_mountpoint);
		commit_tree(child);
		if (q) {
			struct mountpoint *mp = root.mp;
			struct mount *r = child;
@@ -2713,7 +2711,6 @@ static int attach_recursive_mnt(struct mount *source_mnt,
				mp = shorter;
			mnt_change_mountpoint(r, mp, q);
		}
		commit_tree(child);
	}
	unpin_mountpoint(&root);
	unlock_mount_hash();
@@ -2862,6 +2859,19 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
	return attach_recursive_mnt(mnt, p, mp);
}

static int may_change_propagation(const struct mount *m)
{
        struct mnt_namespace *ns = m->mnt_ns;

	 // it must be mounted in some namespace
	 if (IS_ERR_OR_NULL(ns))         // is_mounted()
		 return -EINVAL;
	 // and the caller must be admin in userns of that namespace
	 if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
		 return -EPERM;
	 return 0;
}

/*
 * Sanity check the flags to change_mnt_propagation.
 */
@@ -2898,10 +2908,10 @@ static int do_change_type(struct path *path, int ms_flags)
		return -EINVAL;

	namespace_lock();
	if (!check_mnt(mnt)) {
		err = -EINVAL;
	err = may_change_propagation(mnt);
	if (err)
		goto out_unlock;
	}

	if (type == MS_SHARED) {
		err = invent_group_ids(mnt, recurse);
		if (err)
@@ -3347,18 +3357,11 @@ static int do_set_group(struct path *from_path, struct path *to_path)

	namespace_lock();

	err = -EINVAL;
	/* To and From must be mounted */
	if (!is_mounted(&from->mnt))
		goto out;
	if (!is_mounted(&to->mnt))
		goto out;

	err = -EPERM;
	/* We should be allowed to modify mount namespaces of both mounts */
	if (!ns_capable(from->mnt_ns->user_ns, CAP_SYS_ADMIN))
	err = may_change_propagation(from);
	if (err)
		goto out;
	if (!ns_capable(to->mnt_ns->user_ns, CAP_SYS_ADMIN))
	err = may_change_propagation(to);
	if (err)
		goto out;

	err = -EINVAL;
+6 −4
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ void change_mnt_propagation(struct mount *mnt, int type)
		return;
	}
	if (IS_MNT_SHARED(mnt)) {
		if (type == MS_SLAVE || !hlist_empty(&mnt->mnt_slave_list))
			m = propagation_source(mnt);
		if (list_empty(&mnt->mnt_share)) {
			mnt_release_group_id(mnt);
@@ -637,10 +638,11 @@ void propagate_umount(struct list_head *set)
	}

	// now to_umount consists of all acceptable candidates
	// deal with reparenting of remaining overmounts on those
	// deal with reparenting of surviving overmounts on those
	list_for_each_entry(m, &to_umount, mnt_list) {
		if (m->overmount)
			reparent(m->overmount);
		struct mount *over = m->overmount;
		if (over && !will_be_unmounted(over))
			reparent(over);
	}

	// and fold them into the set