diff options
Diffstat (limited to 'fs/namespace.c')
| -rw-r--r-- | fs/namespace.c | 279 |
1 files changed, 172 insertions, 107 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 854f4fc66469..ae5dc64f8b45 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -321,6 +321,7 @@ static struct mount *alloc_vfsmnt(const char *name) INIT_HLIST_NODE(&mnt->mnt_slave); INIT_HLIST_NODE(&mnt->mnt_mp_list); INIT_HLIST_HEAD(&mnt->mnt_stuck_children); + INIT_HLIST_NODE(&mnt->mnt_ns_visible); RB_CLEAR_NODE(&mnt->mnt_node); mnt->mnt.mnt_idmap = &nop_mnt_idmap; } @@ -1079,7 +1080,7 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt) bool mnt_first_node = true, mnt_last_node = true; WARN_ON(mnt_ns_attached(mnt)); - mnt->mnt_ns = ns; + WRITE_ONCE(mnt->mnt_ns, ns); while (*link) { parent = *link; if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) { @@ -1098,6 +1099,10 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt) rb_link_node(&mnt->mnt_node, parent, link); rb_insert_color(&mnt->mnt_node, &ns->mounts); + if ((mnt->mnt.mnt_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED) && + mnt->mnt.mnt_root == mnt->mnt.mnt_sb->s_root) + hlist_add_head(&mnt->mnt_ns_visible, &ns->mnt_visible_mounts); + mnt_notify_add(mnt); } @@ -1434,7 +1439,7 @@ EXPORT_SYMBOL(mntget); void mnt_make_shortterm(struct vfsmount *mnt) { if (mnt) - real_mount(mnt)->mnt_ns = NULL; + WRITE_ONCE(real_mount(mnt)->mnt_ns, NULL); } /** @@ -1806,7 +1811,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) ns->nr_mounts--; __touch_mnt_namespace(ns); } - p->mnt_ns = NULL; + WRITE_ONCE(p->mnt_ns, NULL); if (how & UMOUNT_SYNC) p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; @@ -2646,6 +2651,19 @@ static int attach_recursive_mnt(struct mount *source_mnt, if (unlikely(shorter) && child != source_mnt) mp = shorter; + /* + * If @q was locked it was meant to hide + * whatever was under it. Let @child take over + * that job and lock it, then we can unlock @q. + * That'll allow another namespace to shed @q + * and reveal @child. Clearly, that mounter + * consented to this by not severing the mount + * relationship. Otherwise, what's the point. + */ + if (IS_MNT_LOCKED(q)) { + child->mnt.mnt_flags |= MNT_LOCKED; + q->mnt.mnt_flags &= ~MNT_LOCKED; + } mnt_change_mountpoint(r, mp, q); } } @@ -2722,7 +2740,7 @@ static inline struct mount *where_to_mount(const struct path *path, * In all cases the location must not have been unmounted and the * chosen mountpoint must be allowed to be mounted on. For "beneath" * case we also require the location to be at the root of a mount - * that has a parent (i.e. is not a root of some namespace). + * that has something mounted on top of it (i.e. has an overmount). */ static void do_lock_mount(const struct path *path, struct pinned_mountpoint *res, @@ -2890,6 +2908,9 @@ static int do_change_type(const struct path *path, int ms_flags) for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) change_mnt_propagation(m, type); + guard(mount_locked_reader)(); + touch_mnt_namespace(mnt->mnt_ns); + return 0; } @@ -2958,10 +2979,9 @@ static inline bool may_copy_tree(const struct path *path) } static struct mount *__do_loopback(const struct path *old_path, - unsigned int flags, unsigned int copy_flags) + bool recurse, unsigned int copy_flags) { struct mount *old = real_mount(old_path->mnt); - bool recurse = flags & AT_RECURSIVE; if (IS_MNT_UNBINDABLE(old)) return ERR_PTR(-EINVAL); @@ -2972,18 +2992,6 @@ static struct mount *__do_loopback(const struct path *old_path, if (!recurse && __has_locked_children(old, old_path->dentry)) return ERR_PTR(-EINVAL); - /* - * When creating a new mount namespace we don't want to copy over - * mounts of mount namespaces to avoid the risk of cycles and also to - * minimize the default complex interdependencies between mount - * namespaces. - * - * We could ofc just check whether all mount namespace files aren't - * creating cycles but really let's keep this simple. - */ - if (!(flags & OPEN_TREE_NAMESPACE)) - copy_flags |= CL_COPY_MNT_NS_FILE; - if (recurse) return copy_tree(old, old_path->dentry, copy_flags); @@ -2998,7 +3006,6 @@ static int do_loopback(const struct path *path, const char *old_name, { struct path old_path __free(path_put) = {}; struct mount *mnt = NULL; - unsigned int flags = recurse ? AT_RECURSIVE : 0; int err; if (!old_name || !*old_name) @@ -3017,7 +3024,7 @@ static int do_loopback(const struct path *path, const char *old_name, if (!check_mnt(mp.parent)) return -EINVAL; - mnt = __do_loopback(&old_path, flags, 0); + mnt = __do_loopback(&old_path, recurse, CL_COPY_MNT_NS_FILE); if (IS_ERR(mnt)) return PTR_ERR(mnt); @@ -3055,7 +3062,7 @@ static struct mnt_namespace *get_detached_copy(const struct path *path, unsigned ns->seq_origin = src_mnt_ns->ns.ns_id; } - mnt = __do_loopback(path, flags, 0); + mnt = __do_loopback(path, (flags & AT_RECURSIVE), CL_COPY_MNT_NS_FILE); if (IS_ERR(mnt)) { emptied_ns = ns; return ERR_CAST(mnt); @@ -3087,7 +3094,13 @@ static struct file *open_detached_copy(struct path *path, unsigned int flags) return file; } -static struct mnt_namespace *create_new_namespace(struct path *path, unsigned int flags) +enum mount_copy_flags_t { + MOUNT_COPY_RECURSIVE = (1 << 0), + MOUNT_COPY_NEW = (1 << 1), +}; + +static struct mnt_namespace *create_new_namespace(struct path *path, + enum mount_copy_flags_t flags) { struct mnt_namespace *ns = current->nsproxy->mnt_ns; struct user_namespace *user_ns = current_user_ns(); @@ -3096,7 +3109,10 @@ static struct mnt_namespace *create_new_namespace(struct path *path, unsigned in struct path to_path; struct mount *mnt; unsigned int copy_flags = 0; - bool locked = false; + bool locked = false, recurse = flags & MOUNT_COPY_RECURSIVE; + + if (unlikely(!d_can_lookup(path->dentry))) + return ERR_PTR(-ENOTDIR); if (user_ns != ns->user_ns) copy_flags |= CL_SLAVE; @@ -3131,11 +3147,14 @@ static struct mnt_namespace *create_new_namespace(struct path *path, unsigned in } /* - * We don't emulate unshare()ing a mount namespace. We stick - * to the restrictions of creating detached bind-mounts. It - * has a lot saner and simpler semantics. + * We don't emulate unshare()ing a mount namespace. We stick to + * the restrictions of creating detached bind-mounts. It has a + * lot saner and simpler semantics. */ - mnt = __do_loopback(path, flags, copy_flags); + if (flags & MOUNT_COPY_NEW) + mnt = clone_mnt(real_mount(path->mnt), path->dentry, copy_flags); + else + mnt = __do_loopback(path, recurse, copy_flags); scoped_guard(mount_writer) { if (IS_ERR(mnt)) { emptied_ns = new_ns; @@ -3164,7 +3183,8 @@ static struct mnt_namespace *create_new_namespace(struct path *path, unsigned in return new_ns; } -static struct file *open_new_namespace(struct path *path, unsigned int flags) +static struct file *open_new_namespace(struct path *path, + enum mount_copy_flags_t flags) { struct mnt_namespace *new_ns; @@ -3217,7 +3237,7 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned return ERR_PTR(ret); if (flags & OPEN_TREE_NAMESPACE) - return open_new_namespace(&path, flags); + return open_new_namespace(&path, (flags & AT_RECURSIVE) ? MOUNT_COPY_RECURSIVE : 0); if (flags & OPEN_TREE_CLONE) return open_detached_copy(&path, flags); @@ -3294,9 +3314,9 @@ static void mnt_warn_timestamp_expiry(const struct path *mountpoint, (ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) { char *buf, *mntpath; - buf = (char *)__get_free_page(GFP_KERNEL); + buf = __getname(); if (buf) - mntpath = d_path(mountpoint, buf, PAGE_SIZE); + mntpath = d_path(mountpoint, buf, PATH_MAX); else mntpath = ERR_PTR(-ENOMEM); if (IS_ERR(mntpath)) @@ -3309,8 +3329,7 @@ static void mnt_warn_timestamp_expiry(const struct path *mountpoint, (unsigned long long)sb->s_time_max); sb->s_iflags |= SB_I_TS_EXPIRY_WARNED; - if (buf) - free_page((unsigned long)buf); + __putname(buf); } } @@ -3465,6 +3484,10 @@ static int do_set_group(const struct path *from_path, const struct path *to_path list_add(&to->mnt_share, &from->mnt_share); set_mnt_shared(to); } + + guard(mount_locked_reader)(); + touch_mnt_namespace(to->mnt_ns); + return 0; } @@ -3513,8 +3536,6 @@ static bool mount_is_ancestor(const struct mount *p1, const struct mount *p2) * @mnt_to: mount under which to mount * @mp: mountpoint of @mnt_to * - * - Make sure that nothing can be mounted beneath the caller's current - * root or the rootfs of the namespace. * - Make sure that the caller can unmount the topmost mount ensuring * that the caller could reveal the underlying mountpoint. * - Ensure that nothing has been mounted on top of @mnt_from before we @@ -3528,26 +3549,14 @@ static bool mount_is_ancestor(const struct mount *p1, const struct mount *p2) */ static int can_move_mount_beneath(const struct mount *mnt_from, const struct mount *mnt_to, - const struct mountpoint *mp) + struct pinned_mountpoint *mp) { struct mount *parent_mnt_to = mnt_to->mnt_parent; - if (IS_MNT_LOCKED(mnt_to)) - return -EINVAL; - /* Avoid creating shadow mounts during mount propagation. */ if (mnt_from->overmount) return -EINVAL; - /* - * Mounting beneath the rootfs only makes sense when the - * semantics of pivot_root(".", ".") are used. - */ - if (&mnt_to->mnt == current->fs->root.mnt) - return -EINVAL; - if (parent_mnt_to == current->nsproxy->mnt_ns->root) - return -EINVAL; - if (mount_is_ancestor(mnt_to, mnt_from)) return -EINVAL; @@ -3557,7 +3566,7 @@ static int can_move_mount_beneath(const struct mount *mnt_from, * propagating a copy @c of @mnt_from on top of @mnt_to. This * defeats the whole purpose of mounting beneath another mount. */ - if (propagation_would_overmount(parent_mnt_to, mnt_to, mp)) + if (propagation_would_overmount(parent_mnt_to, mnt_to, mp->mp)) return -EINVAL; /* @@ -3573,7 +3582,7 @@ static int can_move_mount_beneath(const struct mount *mnt_from, * @mnt_from beneath @mnt_to. */ if (check_mnt(mnt_from) && - propagation_would_overmount(parent_mnt_to, mnt_from, mp)) + propagation_would_overmount(parent_mnt_to, mnt_from, mp->mp)) return -EINVAL; return 0; @@ -3682,7 +3691,7 @@ static int do_move_mount(const struct path *old_path, if (mp.parent != over->mnt_parent) over = mp.parent->overmount; - err = can_move_mount_beneath(old, over, mp.mp); + err = can_move_mount_beneath(old, over, &mp); if (err) return err; } @@ -4231,8 +4240,8 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, struct user_namespace *user_ns, struct fs_struct *new_fs) { struct mnt_namespace *new_ns; - struct vfsmount *rootmnt __free(mntput) = NULL; - struct vfsmount *pwdmnt __free(mntput) = NULL; + struct path old_root __free(path_put) = {}; + struct path old_pwd __free(path_put) = {}; struct mount *p, *q; struct mount *old; struct mount *new; @@ -4252,11 +4261,18 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, return new_ns; guard(namespace_excl)(); - /* First pass: copy the tree topology */ - copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; + + if (flags & CLONE_EMPTY_MNTNS) + copy_flags = 0; + else + copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != ns->user_ns) copy_flags |= CL_SLAVE; - new = copy_tree(old, old->mnt.mnt_root, copy_flags); + + if (flags & CLONE_EMPTY_MNTNS) + new = clone_mnt(old, old->mnt.mnt_root, copy_flags); + else + new = copy_tree(old, old->mnt.mnt_root, copy_flags); if (IS_ERR(new)) { emptied_ns = new_ns; return ERR_CAST(new); @@ -4267,33 +4283,53 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns, } new_ns->root = new; - /* - * Second pass: switch the tsk->fs->* elements and mark new vfsmounts - * as belonging to new namespace. We have already acquired a private - * fs_struct, so tsk->fs->lock is not needed. - */ - p = old; - q = new; - while (p) { - mnt_add_to_ns(new_ns, q); - new_ns->nr_mounts++; + if (flags & CLONE_EMPTY_MNTNS) { + /* + * Empty mount namespace: only the root mount exists. + * Reset root and pwd to the cloned mount's root dentry. + */ if (new_fs) { - if (&p->mnt == new_fs->root.mnt) { - new_fs->root.mnt = mntget(&q->mnt); - rootmnt = &p->mnt; - } - if (&p->mnt == new_fs->pwd.mnt) { - new_fs->pwd.mnt = mntget(&q->mnt); - pwdmnt = &p->mnt; + old_root = new_fs->root; + old_pwd = new_fs->pwd; + + new_fs->root.mnt = mntget(&new->mnt); + new_fs->root.dentry = dget(new->mnt.mnt_root); + + new_fs->pwd.mnt = mntget(&new->mnt); + new_fs->pwd.dentry = dget(new->mnt.mnt_root); + } + mnt_add_to_ns(new_ns, new); + new_ns->nr_mounts++; + } else { + /* + * Full copy: walk old and new trees in parallel, switching + * the tsk->fs->* elements and marking new vfsmounts as + * belonging to new namespace. We have already acquired a + * private fs_struct, so tsk->fs->lock is not needed. + */ + p = old; + q = new; + while (p) { + mnt_add_to_ns(new_ns, q); + new_ns->nr_mounts++; + if (new_fs) { + if (&p->mnt == new_fs->root.mnt) { + old_root.mnt = new_fs->root.mnt; + new_fs->root.mnt = mntget(&q->mnt); + } + if (&p->mnt == new_fs->pwd.mnt) { + old_pwd.mnt = new_fs->pwd.mnt; + new_fs->pwd.mnt = mntget(&q->mnt); + } } + p = next_mnt(p, old); + q = next_mnt(q, new); + if (!q) + break; + // an mntns binding we'd skipped? + while (p->mnt.mnt_root != q->mnt.mnt_root) + p = next_mnt(skip_mnt_tree(p), old); } - p = next_mnt(p, old); - q = next_mnt(q, new); - if (!q) - break; - // an mntns binding we'd skipped? - while (p->mnt.mnt_root != q->mnt.mnt_root) - p = next_mnt(skip_mnt_tree(p), old); } ns_tree_add_raw(new_ns); return new_ns; @@ -4414,11 +4450,15 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, unsigned int mnt_flags = 0; long ret; - if (!may_mount()) + if ((flags & ~(FSMOUNT_CLOEXEC | FSMOUNT_NAMESPACE)) != 0) + return -EINVAL; + + if ((flags & FSMOUNT_NAMESPACE) && + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; - if ((flags & ~(FSMOUNT_CLOEXEC)) != 0) - return -EINVAL; + if (!(flags & FSMOUNT_NAMESPACE) && !may_mount()) + return -EPERM; if (attr_flags & ~FSMOUNT_VALID_FLAGS) return -EINVAL; @@ -4473,6 +4513,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, new_mnt = vfs_create_mount(fc); if (IS_ERR(new_mnt)) return PTR_ERR(new_mnt); + if (new_mnt->mnt_sb->s_flags & SB_NOUSER) { + mntput(new_mnt); + return -EINVAL; + } new_mnt->mnt_flags = mnt_flags; new_path.dentry = dget(fc->root); @@ -4485,6 +4529,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, */ vfs_clean_context(fc); + if (flags & FSMOUNT_NAMESPACE) + return FD_ADD((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0, + open_new_namespace(&new_path, MOUNT_COPY_NEW)); + ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); if (IS_ERR(ns)) return PTR_ERR(ns); @@ -5649,14 +5697,14 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root) if (mnt_ns_empty(ns)) return -ENOENT; - first = child = ns->root; - for (;;) { - child = listmnt_next(child, false); - if (!child) - return -ENOENT; - if (child->mnt_parent == first) + first = ns->root; + for (child = node_to_mount(ns->mnt_first_node); child; + child = listmnt_next(child, false)) { + if (child != first && child->mnt_parent == first) break; } + if (!child) + return -ENOENT; root->mnt = mntget(&child->mnt); root->dentry = dget(root->mnt->mnt_root); @@ -5951,7 +5999,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, return -EPERM; } - ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); + ks = kmalloc_obj(*ks, GFP_KERNEL_ACCOUNT); if (!ks) return -ENOMEM; @@ -6143,12 +6191,14 @@ static void __init init_mount_tree(void) struct path root; /* - * We create two mounts: + * We create three mounts: * * (1) nullfs with mount id 1 * (2) mutable rootfs with mount id 2 + * (3) private nullfs for kthreads (SB_KERNMOUNT) * - * with (2) mounted on top of (1). + * with (2) mounted on top of (1). The init_task's root and pwd + * are pointed at (3) so all kthreads start isolated in nullfs. */ nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL); if (IS_ERR(nullfs_mnt)) @@ -6188,12 +6238,14 @@ static void __init init_mount_tree(void) init_mnt_ns.nr_mounts++; } + nullfs_mnt = kern_mount(&nullfs_fs_type); + if (IS_ERR(nullfs_mnt)) + panic("VFS: Failed to create private nullfs instance"); + root.mnt = nullfs_mnt; + root.dentry = nullfs_mnt->mnt_root; + init_task.nsproxy->mnt_ns = &init_mnt_ns; get_mnt_ns(&init_mnt_ns); - - /* The root and pwd always point to the mutable rootfs. */ - root.mnt = mnt; - root.dentry = mnt->mnt_root; set_fs_pwd(current->fs, &root); set_fs_root(current->fs, &root); @@ -6218,8 +6270,7 @@ void __init mnt_init(void) HASH_ZERO, &mp_hash_shift, &mp_hash_mask, 0, 0); - if (!mount_hashtable || !mountpoint_hashtable) - panic("Failed to allocate mount hash table\n"); + super_dev_init(); kernfs_init(); @@ -6233,6 +6284,7 @@ void __init mnt_init(void) shmem_init(); init_rootfs(); init_mount_tree(); + failfs_init(); } void put_mnt_ns(struct mnt_namespace *ns) @@ -6242,7 +6294,7 @@ void put_mnt_ns(struct mnt_namespace *ns) guard(namespace_excl)(); emptied_ns = ns; guard(mount_writer)(); - umount_tree(ns->root, 0); + umount_tree(ns->root, UMOUNT_CONNECTED); } struct vfsmount *kern_mount(struct file_system_type *type) @@ -6310,20 +6362,26 @@ static bool mnt_already_visible(struct mnt_namespace *ns, int *new_mnt_flags) { int new_flags = *new_mnt_flags; - struct mount *mnt, *n; + struct mount *mnt; + + /* Don't acquire namespace semaphore without a good reason. */ + if (hlist_empty(&ns->mnt_visible_mounts)) + return false; guard(namespace_shared)(); - rbtree_postorder_for_each_entry_safe(mnt, n, &ns->mounts, mnt_node) { + hlist_for_each_entry(mnt, &ns->mnt_visible_mounts, mnt_ns_visible) { + const struct super_block *sb_visible = mnt->mnt.mnt_sb; struct mount *child; int mnt_flags; - if (mnt->mnt.mnt_sb->s_type != sb->s_type) + if (sb_visible->s_type != sb->s_type) continue; - /* This mount is not fully visible if it's root directory - * is not the root directory of the filesystem. + /* + * Restricted variants are not compatible with anything, even + * other restricted variants. */ - if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root) + if (sb_visible->s_iflags & SB_I_RESTRICTED_VARIANT) continue; /* A local view of the mount flags */ @@ -6375,16 +6433,23 @@ static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags return false; /* Can this filesystem be too revealing? */ - s_iflags = sb->s_iflags; - if (!(s_iflags & SB_I_USERNS_VISIBLE)) + if (!(sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED)) return false; + s_iflags = sb->s_iflags; if ((s_iflags & required_iflags) != required_iflags) { WARN_ONCE(1, "Expected s_iflags to contain 0x%lx\n", required_iflags); return true; } + /* + * Restricted variants don't need an already visible mount because they + * don't expose the full filesystem view. + */ + if (s_iflags & SB_I_RESTRICTED_VARIANT) + return false; + return !mnt_already_visible(ns, sb, new_mnt_flags); } |
