| From 427215d85e8d1476da1a86b8d67aceb485eb3631 Mon Sep 17 00:00:00 2001 |
| From: Miklos Szeredi <mszeredi@redhat.com> |
| Date: Mon, 9 Aug 2021 10:19:47 +0200 |
| Subject: ovl: prevent private clone if bind mount is not allowed |
| |
| From: Miklos Szeredi <mszeredi@redhat.com> |
| |
| 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: Alois Wohlschlager <alois1@gmx-topmail.de> |
| Fixes: c771d683a62e ("vfs: introduce clone_private_mount()") |
| Cc: <stable@vger.kernel.org> # v3.18 |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/namespace.c | 42 ++++++++++++++++++++++++++++-------------- |
| 1 file changed, 28 insertions(+), 14 deletions(-) |
| |
| --- a/fs/namespace.c |
| +++ b/fs/namespace.c |
| @@ -1919,6 +1919,20 @@ void drop_collected_mounts(struct vfsmou |
| 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 |
| * |
| @@ -1933,10 +1947,19 @@ struct vfsmount *clone_private_mount(con |
| struct mount *old_mnt = real_mount(path->mnt); |
| struct mount *new_mnt; |
| |
| + down_read(&namespace_sem); |
| 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); |
| + up_read(&namespace_sem); |
| + |
| if (IS_ERR(new_mnt)) |
| return ERR_CAST(new_mnt); |
| |
| @@ -1944,6 +1967,10 @@ struct vfsmount *clone_private_mount(con |
| new_mnt->mnt_ns = MNT_NS_INTERNAL; |
| |
| return &new_mnt->mnt; |
| + |
| +invalid: |
| + up_read(&namespace_sem); |
| + return ERR_PTR(-EINVAL); |
| } |
| EXPORT_SYMBOL_GPL(clone_private_mount); |
| |
| @@ -2295,19 +2322,6 @@ static int do_change_type(struct path *p |
| 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) |
| { |
| struct mount *mnt = ERR_PTR(-EINVAL), *old = real_mount(old_path->mnt); |