]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ceph: fix NULL pointer dereference in ceph_mds_auth_match()
authorViacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Tue, 3 Feb 2026 22:54:46 +0000 (14:54 -0800)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 4 Feb 2026 21:00:42 +0000 (22:00 +0100)
The CephFS kernel client has regression starting from 6.18-rc1.
We have issue in ceph_mds_auth_match() if fs_name == NULL:

    const char fs_name = mdsc->fsc->mount_options->mds_namespace;
    ...
    if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {
            / fsname mismatch, try next one */
            return 0;
    }

Patrick Donnelly suggested that: In summary, we should definitely start
decoding `fs_name` from the MDSMap and do strict authorizations checks
against it. Note that the `-o mds_namespace=foo` should only be used for
selecting the file system to mount and nothing else. It's possible
no mds_namespace is specified but the kernel will mount the only
file system that exists which may have name "foo".

This patch reworks ceph_mdsmap_decode() and namespace_equals() with
the goal of supporting the suggested concept. Now struct ceph_mdsmap
contains m_fs_name field that receives copy of extracted FS name
by ceph_extract_encoded_string(). For the case of "old" CephFS file
systems, it is used "cephfs" name.

[ idryomov: replace redundant %*pE with %s in ceph_mdsmap_decode(),
  get rid of a series of strlen() calls in ceph_namespace_match(),
  drop changes to namespace_equals() body to avoid treating empty
  mds_namespace as equal, drop changes to ceph_mdsc_handle_fsmap()
  as namespace_equals() isn't an equivalent substitution there ]

Cc: stable@vger.kernel.org
Fixes: 22c73d52a6d0 ("ceph: fix multifs mds auth caps issue")
Link: https://tracker.ceph.com/issues/73886
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Reviewed-by: Patrick Donnelly <pdonnell@ibm.com>
Tested-by: Patrick Donnelly <pdonnell@ibm.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/mds_client.c
fs/ceph/mdsmap.c
fs/ceph/mdsmap.h
fs/ceph/super.h
include/linux/ceph/ceph_fs.h

index 7e4eab824daef88c8cdc0257bf686239f0f29674..c45bd19d4b1ca73de28750d08ed488ada22824f9 100644 (file)
@@ -5671,7 +5671,7 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
        u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
        u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
        struct ceph_client *cl = mdsc->fsc->client;
-       const char *fs_name = mdsc->fsc->mount_options->mds_namespace;
+       const char *fs_name = mdsc->mdsmap->m_fs_name;
        const char *spath = mdsc->fsc->mount_options->server_path;
        bool gid_matched = false;
        u32 gid, tlen, len;
@@ -5679,7 +5679,8 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
 
        doutc(cl, "fsname check fs_name=%s  match.fs_name=%s\n",
              fs_name, auth->match.fs_name ? auth->match.fs_name : "");
-       if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {
+
+       if (!ceph_namespace_match(auth->match.fs_name, fs_name)) {
                /* fsname mismatch, try next one */
                return 0;
        }
index 2c7b151a7c95cc16db75ce1a8b759221b9027042..b228e5ecfb926d9f5f989286ea235771b1028aef 100644 (file)
@@ -353,22 +353,33 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
                __decode_and_drop_type(p, end, u8, bad_ext);
        }
        if (mdsmap_ev >= 8) {
-               u32 fsname_len;
+               size_t fsname_len;
+
                /* enabled */
                ceph_decode_8_safe(p, end, m->m_enabled, bad_ext);
+
                /* fs_name */
-               ceph_decode_32_safe(p, end, fsname_len, bad_ext);
+               m->m_fs_name = ceph_extract_encoded_string(p, end,
+                                                          &fsname_len,
+                                                          GFP_NOFS);
+               if (IS_ERR(m->m_fs_name)) {
+                       m->m_fs_name = NULL;
+                       goto nomem;
+               }
 
                /* validate fsname against mds_namespace */
-               if (!namespace_equals(mdsc->fsc->mount_options, *p,
+               if (!namespace_equals(mdsc->fsc->mount_options, m->m_fs_name,
                                      fsname_len)) {
-                       pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n",
-                                      (int)fsname_len, (char *)*p,
+                       pr_warn_client(cl, "fsname %s doesn't match mds_namespace %s\n",
+                                      m->m_fs_name,
                                       mdsc->fsc->mount_options->mds_namespace);
                        goto bad;
                }
-               /* skip fsname after validation */
-               ceph_decode_skip_n(p, end, fsname_len, bad);
+       } else {
+               m->m_enabled = false;
+               m->m_fs_name = kstrdup(CEPH_OLD_FS_NAME, GFP_NOFS);
+               if (!m->m_fs_name)
+                       goto nomem;
        }
        /* damaged */
        if (mdsmap_ev >= 9) {
@@ -430,6 +441,7 @@ void ceph_mdsmap_destroy(struct ceph_mdsmap *m)
                kfree(m->m_info);
        }
        kfree(m->m_data_pg_pools);
+       kfree(m->m_fs_name);
        kfree(m);
 }
 
index 1f2171dd01bfa34a404eef00113646bdcb978980..d48d07c3516d447a8f3e684f6ded1f1ca1674417 100644 (file)
@@ -45,6 +45,7 @@ struct ceph_mdsmap {
        bool m_enabled;
        bool m_damaged;
        int m_num_laggy;
+       char *m_fs_name;
 };
 
 static inline struct ceph_entity_addr *
index a1f781c46b41d3f3eb06a3f121d7e321d8c517c2..29a980e22dc26c7ae83391505c3ac5249d325bd3 100644 (file)
@@ -104,14 +104,26 @@ struct ceph_mount_options {
        struct fscrypt_dummy_policy dummy_enc_policy;
 };
 
+#define CEPH_NAMESPACE_WILDCARD                "*"
+
+static inline bool ceph_namespace_match(const char *pattern,
+                                       const char *target)
+{
+       if (!pattern || !pattern[0] ||
+           !strcmp(pattern, CEPH_NAMESPACE_WILDCARD))
+               return true;
+
+       return !strcmp(pattern, target);
+}
+
 /*
  * Check if the mds namespace in ceph_mount_options matches
  * the passed in namespace string. First time match (when
  * ->mds_namespace is NULL) is treated specially, since
  * ->mds_namespace needs to be initialized by the caller.
  */
-static inline int namespace_equals(struct ceph_mount_options *fsopt,
-                                  const char *namespace, size_t len)
+static inline bool namespace_equals(struct ceph_mount_options *fsopt,
+                                   const char *namespace, size_t len)
 {
        return !(fsopt->mds_namespace &&
                 (strlen(fsopt->mds_namespace) != len ||
index c7f2c63b3bc3fb3c6a1ec0d114fcaf9cd7489de3..08e5dbe15ca4446081779f035d64f0a6bc34b382 100644 (file)
 #define CEPH_INO_CEPH   2            /* hidden .ceph dir */
 #define CEPH_INO_GLOBAL_SNAPREALM  3 /* global dummy snaprealm */
 
+/*
+ * name for "old" CephFS file systems,
+ * see ceph.git e2b151d009640114b2565c901d6f41f6cd5ec652
+ */
+#define CEPH_OLD_FS_NAME       "cephfs"
+
 /* arbitrary limit on max # of monitors (cluster of 3 is typical) */
 #define CEPH_MAX_MON   31