]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fs: use guard for namespace_sem in statmount()
authorChristian Brauner <brauner@kernel.org>
Tue, 25 Jun 2024 12:33:45 +0000 (14:33 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 28 Jun 2024 12:36:43 +0000 (14:36 +0200)
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/namespace.c

index e871f73c4c8c4ff6dc7c47846bcabcd6da1a2b6c..a989e89b0a10d039c0b5a50e1f4ee916e0fd9a32 100644 (file)
@@ -5040,17 +5040,70 @@ static int copy_statmount_to_user(struct kstatmount *s)
        return 0;
 }
 
-static int do_statmount(struct kstatmount *s)
+static struct mount *listmnt_next(struct mount *curr, bool reverse)
 {
-       struct mount *m = real_mount(s->mnt);
-       struct mnt_namespace *ns = m->mnt_ns;
+       struct rb_node *node;
+
+       if (reverse)
+               node = rb_prev(&curr->mnt_node);
+       else
+               node = rb_next(&curr->mnt_node);
+
+       return node_to_mount(node);
+}
+
+static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
+{
+       struct mount *first;
+
+       rwsem_assert_held(&namespace_sem);
+
+       /* We're looking at our own ns, just use get_fs_root. */
+       if (ns == current->nsproxy->mnt_ns) {
+               get_fs_root(current->fs, root);
+               return 0;
+       }
+
+       /*
+        * We have to find the first mount in our ns and use that, however it
+        * may not exist, so handle that properly.
+        */
+       if (RB_EMPTY_ROOT(&ns->mounts))
+               return -ENOENT;
+
+       first = listmnt_next(ns->root, false);
+       if (!first)
+               return -ENOENT;
+       root->mnt = mntget(&first->mnt);
+       root->dentry = dget(root->mnt->mnt_root);
+       return 0;
+}
+
+static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
+                       struct mnt_namespace *ns)
+{
+       struct path root __free(path_put) = {};
+       struct mount *m;
        int err;
 
+       /* Has the namespace already been emptied? */
+       if (mnt_ns_id && RB_EMPTY_ROOT(&ns->mounts))
+               return -ENOENT;
+
+       s->mnt = lookup_mnt_in_ns(mnt_id, ns);
+       if (!s->mnt)
+               return -ENOENT;
+
+       err = grab_requested_root(ns, &root);
+       if (err)
+               return err;
+
        /*
         * Don't trigger audit denials. We just want to determine what
         * mounts to show users.
         */
-       if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
+       m = real_mount(s->mnt);
+       if (!is_path_reachable(m, m->mnt.mnt_root, &root) &&
            !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
                return -EPERM;
 
@@ -5058,6 +5111,7 @@ static int do_statmount(struct kstatmount *s)
        if (err)
                return err;
 
+       s->root = root;
        if (s->mask & STATMOUNT_SB_BASIC)
                statmount_sb_basic(s);
 
@@ -5096,6 +5150,9 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
        return true;
 }
 
+#define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
+                             STATMOUNT_FS_TYPE)
+
 static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
                              struct statmount __user *buf, size_t bufsize,
                              size_t seq_size)
@@ -5107,10 +5164,18 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
        ks->mask = kreq->param;
        ks->buf = buf;
        ks->bufsize = bufsize;
-       ks->seq.size = seq_size;
-       ks->seq.buf = kvmalloc(seq_size, GFP_KERNEL_ACCOUNT);
-       if (!ks->seq.buf)
-               return -ENOMEM;
+
+       if (ks->mask & STATMOUNT_STRING_REQ) {
+               if (bufsize == sizeof(ks->sm))
+                       return -EOVERFLOW;
+
+               ks->seq.buf = kvmalloc(seq_size, GFP_KERNEL_ACCOUNT);
+               if (!ks->seq.buf)
+                       return -ENOMEM;
+
+               ks->seq.size = seq_size;
+       }
+
        return 0;
 }
 
@@ -5138,45 +5203,6 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
        return 0;
 }
 
-static struct mount *listmnt_next(struct mount *curr, bool reverse)
-{
-       struct rb_node *node;
-
-       if (reverse)
-               node = rb_prev(&curr->mnt_node);
-       else
-               node = rb_next(&curr->mnt_node);
-
-       return node_to_mount(node);
-}
-
-static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
-{
-       struct mount *first;
-
-       rwsem_assert_held(&namespace_sem);
-
-       /* We're looking at our own ns, just use get_fs_root. */
-       if (ns == current->nsproxy->mnt_ns) {
-               get_fs_root(current->fs, root);
-               return 0;
-       }
-
-       /*
-        * We have to find the first mount in our ns and use that, however it
-        * may not exist, so handle that properly.
-        */
-       if (RB_EMPTY_ROOT(&ns->mounts))
-               return -ENOENT;
-
-       first = listmnt_next(ns->root, false);
-       if (!first)
-               return -ENOENT;
-       root->mnt = mntget(&first->mnt);
-       root->dentry = dget(root->mnt->mnt_root);
-       return 0;
-}
-
 /*
  * If the user requested a specific mount namespace id, look that up and return
  * that, or if not simply grab a passive reference on our mount namespace and
@@ -5195,9 +5221,8 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
                unsigned int, flags)
 {
        struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
-       struct vfsmount *mnt;
+       struct kstatmount *ks __free(kfree) = NULL;
        struct mnt_id_req kreq;
-       struct kstatmount ks;
        /* We currently support retrieval of 3 strings. */
        size_t seq_size = 3 * PATH_MAX;
        int ret;
@@ -5217,40 +5242,21 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
            !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
                return -ENOENT;
 
+       ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT);
+       if (!ks)
+               return -ENOMEM;
+
 retry:
-       ret = prepare_kstatmount(&ks, &kreq, buf, bufsize, seq_size);
+       ret = prepare_kstatmount(ks, &kreq, buf, bufsize, seq_size);
        if (ret)
                return ret;
 
-       down_read(&namespace_sem);
-       /* Has the namespace already been emptied? */
-       if (kreq.mnt_ns_id && RB_EMPTY_ROOT(&ns->mounts)) {
-               up_read(&namespace_sem);
-               kvfree(ks.seq.buf);
-               return -ENOENT;
-       }
-
-       mnt = lookup_mnt_in_ns(kreq.mnt_id, ns);
-       if (!mnt) {
-               up_read(&namespace_sem);
-               kvfree(ks.seq.buf);
-               return -ENOENT;
-       }
-
-       ks.mnt = mnt;
-       ret = grab_requested_root(ns, &ks.root);
-       if (ret) {
-               up_read(&namespace_sem);
-               kvfree(ks.seq.buf);
-               return ret;
-       }
-       ret = do_statmount(&ks);
-       path_put(&ks.root);
-       up_read(&namespace_sem);
+       scoped_guard(rwsem_read, &namespace_sem)
+               ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns);
 
        if (!ret)
-               ret = copy_statmount_to_user(&ks);
-       kvfree(ks.seq.buf);
+               ret = copy_statmount_to_user(ks);
+       kvfree(ks->seq.buf);
        if (retry_statmount(ret, &seq_size))
                goto retry;
        return ret;