One RBD and two CephFS fixes which address potential oopses. The RBD

thing is more of a rare edge case that pops up in our CI, while the two
 CephFS scenarios are regressions that were reported by users and can be
 triggered trivially in normal operation.  All marked for stable.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCAAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAmmGC6kTHGlkcnlvbW92
 QGdtYWlsLmNvbQAKCRBKf944AhHzi+csB/9WgySi6UKkZM6w9A5HnkUnCHXf8cS2
 RuYv0kBB0zv9SorBipve6PdxVQoqyCc6wuwbiUUhKGmwfQp6ubguh4vPK5zX98Cl
 tjPcDDUzmTvWvOgr3NEzD3T838hkS/K0FocR+ratoMoZ2Hl8K8NSXj53vAFrHT49
 v0hP5YS3UaNeqSA4NjFeMtEoRWDJJrUyim5xtZWAAlL7gEsPkHzmx1NlMYa5Qe22
 mmgjcU29zKXjVs96rQqAu0W5jL+M7TJfssy/Pof7t2wVV15F0VSrdqU6KlmLNDOT
 P8QMolYD6WlEG8RY/ryBzhS6eqTr4fbREuJLLVzLxPQKDeJrzyhumT++
 =9NmM
 -----END PGP SIGNATURE-----

Merge tag 'ceph-for-6.19-rc9' of https://github.com/ceph/ceph-client

Pull ceph fixes from Ilya Dryomov:
 "One RBD and two CephFS fixes which address potential oopses.

  The RBD thing is more of a rare edge case that pops up in our CI,
  while the two CephFS scenarios are regressions that were reported by
  users and can be triggered trivially in normal operation. All marked
  for stable"

* tag 'ceph-for-6.19-rc9' of https://github.com/ceph/ceph-client:
  ceph: fix NULL pointer dereference in ceph_mds_auth_match()
  ceph: fix oops due to invalid pointer for kfree() in parse_longname()
  rbd: check for EOD after exclusive lock is ensured to be held
This commit is contained in:
Linus Torvalds 2026-02-06 10:34:17 -08:00
commit 240b8d8227
7 changed files with 69 additions and 27 deletions

View File

@ -3495,11 +3495,29 @@ static void rbd_img_object_requests(struct rbd_img_request *img_req)
rbd_assert(!need_exclusive_lock(img_req) ||
__rbd_is_lock_owner(rbd_dev));
if (rbd_img_is_write(img_req)) {
rbd_assert(!img_req->snapc);
if (test_bit(IMG_REQ_CHILD, &img_req->flags)) {
rbd_assert(!rbd_img_is_write(img_req));
} else {
struct request *rq = blk_mq_rq_from_pdu(img_req);
u64 off = (u64)blk_rq_pos(rq) << SECTOR_SHIFT;
u64 len = blk_rq_bytes(rq);
u64 mapping_size;
down_read(&rbd_dev->header_rwsem);
img_req->snapc = ceph_get_snap_context(rbd_dev->header.snapc);
mapping_size = rbd_dev->mapping.size;
if (rbd_img_is_write(img_req)) {
rbd_assert(!img_req->snapc);
img_req->snapc =
ceph_get_snap_context(rbd_dev->header.snapc);
}
up_read(&rbd_dev->header_rwsem);
if (unlikely(off + len > mapping_size)) {
rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)",
off, len, mapping_size);
img_req->pending.result = -EIO;
return;
}
}
for_each_obj_request(img_req, obj_req) {
@ -4725,7 +4743,6 @@ static void rbd_queue_workfn(struct work_struct *work)
struct request *rq = blk_mq_rq_from_pdu(img_request);
u64 offset = (u64)blk_rq_pos(rq) << SECTOR_SHIFT;
u64 length = blk_rq_bytes(rq);
u64 mapping_size;
int result;
/* Ignore/skip any zero-length requests */
@ -4738,17 +4755,9 @@ static void rbd_queue_workfn(struct work_struct *work)
blk_mq_start_request(rq);
down_read(&rbd_dev->header_rwsem);
mapping_size = rbd_dev->mapping.size;
rbd_img_capture_header(img_request);
up_read(&rbd_dev->header_rwsem);
if (offset + length > mapping_size) {
rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)", offset,
length, mapping_size);
result = -EIO;
goto err_img_request;
}
dout("%s rbd_dev %p img_req %p %s %llu~%llu\n", __func__, rbd_dev,
img_request, obj_op_name(op_type), offset, length);

View File

@ -166,12 +166,13 @@ static struct inode *parse_longname(const struct inode *parent,
struct ceph_vino vino = { .snap = CEPH_NOSNAP };
char *name_end, *inode_number;
int ret = -EIO;
/* NUL-terminate */
char *str __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL);
/* Snapshot name must start with an underscore */
if (*name_len <= 0 || name[0] != '_')
return ERR_PTR(-EIO);
/* Skip initial '_' and NUL-terminate */
char *str __free(kfree) = kmemdup_nul(name + 1, *name_len - 1, GFP_KERNEL);
if (!str)
return ERR_PTR(-ENOMEM);
/* Skip initial '_' */
str++;
name_end = strrchr(str, '_');
if (!name_end) {
doutc(cl, "failed to parse long snapshot name: %s\n", str);

View 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;
}

View 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);
}

View 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 *

View 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 ||

View File

@ -31,6 +31,12 @@
#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