Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 812f39ed authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Greg Kroah-Hartman
Browse files

ovl: prevent private clone if bind mount is not allowed



commit 427215d85e8d1476da1a86b8d67aceb485eb3631 upstream.

Add the following checks from __do_loopback() to clone_private_mount() as
well:

 - verify that the mount is in the current namespace

 - verify that there are no locked children

Reported-by: default avatarAlois Wohlschlager <alois1@gmx-topmail.de>
Fixes: c771d683 ("vfs: introduce clone_private_mount()")
Cc: <stable@vger.kernel.org> # v3.18
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent eeb47425
Loading
Loading
Loading
Loading
+28 −14
Original line number Original line Diff line number Diff line
@@ -1861,6 +1861,20 @@ void drop_collected_mounts(struct vfsmount *mnt)
	namespace_unlock();
	namespace_unlock();
}
}


static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
{
	struct mount *child;

	list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
		if (!is_subdir(child->mnt_mountpoint, dentry))
			continue;

		if (child->mnt.mnt_flags & MNT_LOCKED)
			return true;
	}
	return false;
}

/**
/**
 * clone_private_mount - create a private clone of a path
 * clone_private_mount - create a private clone of a path
 *
 *
@@ -1875,14 +1889,27 @@ struct vfsmount *clone_private_mount(const struct path *path)
	struct mount *old_mnt = real_mount(path->mnt);
	struct mount *old_mnt = real_mount(path->mnt);
	struct mount *new_mnt;
	struct mount *new_mnt;


	down_read(&namespace_sem);
	if (IS_MNT_UNBINDABLE(old_mnt))
	if (IS_MNT_UNBINDABLE(old_mnt))
		return ERR_PTR(-EINVAL);
		goto invalid;

	if (!check_mnt(old_mnt))
		goto invalid;

	if (has_locked_children(old_mnt, path->dentry))
		goto invalid;


	new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE);
	new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE);
	up_read(&namespace_sem);

	if (IS_ERR(new_mnt))
	if (IS_ERR(new_mnt))
		return ERR_CAST(new_mnt);
		return ERR_CAST(new_mnt);


	return &new_mnt->mnt;
	return &new_mnt->mnt;

invalid:
	up_read(&namespace_sem);
	return ERR_PTR(-EINVAL);
}
}
EXPORT_SYMBOL_GPL(clone_private_mount);
EXPORT_SYMBOL_GPL(clone_private_mount);


@@ -2234,19 +2261,6 @@ static int do_change_type(struct path *path, int ms_flags)
	return err;
	return err;
}
}


static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
{
	struct mount *child;
	list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
		if (!is_subdir(child->mnt_mountpoint, dentry))
			continue;

		if (child->mnt.mnt_flags & MNT_LOCKED)
			return true;
	}
	return false;
}

static struct mount *__do_loopback(struct path *old_path, int recurse)
static struct mount *__do_loopback(struct path *old_path, int recurse)
{
{
	struct mount *mnt = ERR_PTR(-EINVAL), *old = real_mount(old_path->mnt);
	struct mount *mnt = ERR_PTR(-EINVAL), *old = real_mount(old_path->mnt);