Pull ceph fixes from Ilya Dryomov:
 "An important patch from Hristo that squashes a folio reference leak
  that could lead to OOM kills in CephFS and a number of miscellaneous
  fixes from Raphael and Slava.

  All but two are marked for stable"

* tag 'ceph-for-7.1-rc4' of https://github.com/ceph/ceph-client:
  libceph: Fix potential null-ptr-deref in decode_choose_args()
  libceph: handle rbtree insertion error in decode_choose_args()
  libceph: Fix potential out-of-bounds access in osdmap_decode()
  ceph: put folios not suitable for writeback
  ceph: add ceph_has_realms_with_quotas() check to ceph_quota_update_statfs()
  libceph: Fix potential out-of-bounds access in __ceph_x_decrypt()
  ceph: fix BUG_ON in __ceph_build_xattrs_blob() due to stale blob size
  ceph: fix a buffer leak in __ceph_setxattr()
  libceph: Fix unnecessarily high ceph_decode_need() for uniform bucket
  libceph: Fix potential out-of-bounds access in crush_decode()
This commit is contained in:
Linus Torvalds
2026-05-15 14:48:09 -07:00
6 changed files with 64 additions and 20 deletions

View File

@@ -1336,6 +1336,7 @@ void ceph_process_folio_batch(struct address_space *mapping,
ceph_wbc, folio);
if (rc == -ENODATA) {
folio_unlock(folio);
folio_put(folio);
ceph_wbc->fbatch.folios[i] = NULL;
continue;
} else if (rc == -E2BIG) {
@@ -1346,6 +1347,7 @@ void ceph_process_folio_batch(struct address_space *mapping,
if (!folio_clear_dirty_for_io(folio)) {
doutc(cl, "%p !folio_clear_dirty_for_io\n", folio);
folio_unlock(folio);
folio_put(folio);
ceph_wbc->fbatch.folios[i] = NULL;
continue;
}

View File

@@ -228,12 +228,19 @@ static int get_quota_realm(struct ceph_mds_client *mdsc, struct inode *inode,
restart:
realm = ceph_inode(inode)->i_snap_realm;
if (realm)
if (realm) {
ceph_get_snap_realm(mdsc, realm);
else
pr_err_ratelimited_client(cl,
"%p %llx.%llx null i_snap_realm\n",
inode, ceph_vinop(inode));
} else {
/*
* i_snap_realm is NULL when all caps have been released, e.g.
* after an MDS session rejection. This is a transient state;
* the realm will be restored once caps are re-granted.
* Treat it as "no quota realm found".
*/
doutc(cl, "%p %llx.%llx null i_snap_realm\n",
inode, ceph_vinop(inode));
}
while (realm) {
bool has_inode;
@@ -340,12 +347,19 @@ static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
down_read(&mdsc->snap_rwsem);
restart:
realm = ceph_inode(inode)->i_snap_realm;
if (realm)
if (realm) {
ceph_get_snap_realm(mdsc, realm);
else
pr_err_ratelimited_client(cl,
"%p %llx.%llx null i_snap_realm\n",
inode, ceph_vinop(inode));
} else {
/*
* i_snap_realm is NULL when all caps have been released, e.g.
* after an MDS session rejection. This is a transient state;
* the realm will be restored once caps are re-granted.
* Treat it as "quota not exceeded".
*/
doutc(cl, "%p %llx.%llx null i_snap_realm\n",
inode, ceph_vinop(inode));
}
while (realm) {
bool has_inode;
@@ -496,6 +510,9 @@ bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
u64 total = 0, used, free;
bool is_updated = false;
if (!ceph_has_realms_with_quotas(d_inode(fsc->sb->s_root)))
return false;
down_read(&mdsc->snap_rwsem);
get_quota_realm(mdsc, d_inode(fsc->sb->s_root), QUOTA_GET_MAX_BYTES,
&realm, true);

View File

@@ -1254,6 +1254,22 @@ int __ceph_setxattr(struct inode *inode, const char *name,
ceph_vinop(inode), name, ceph_cap_string(issued));
__build_xattrs(inode);
/*
* __build_xattrs() may have released and reacquired i_ceph_lock,
* during which handle_cap_grant() could have replaced i_xattrs.blob
* with a newer MDS-provided blob and bumped i_xattrs.version. If that
* caused __build_xattrs() to rebuild the rb-tree from the new blob,
* count/names_size/vals_size may now be larger than when
* required_blob_size was computed above. Recompute it here so the
* prealloc_blob size check below reflects the current tree state.
*/
required_blob_size = __get_required_blob_size(ci, name_len, val_len);
if (required_blob_size > mdsc->mdsmap->m_max_xattr_size) {
doutc(cl, "sync (size too large): %d > %llu\n",
required_blob_size, mdsc->mdsmap->m_max_xattr_size);
goto do_sync;
}
if (!ci->i_xattrs.prealloc_blob ||
required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) {
struct ceph_buffer *blob;
@@ -1294,6 +1310,7 @@ int __ceph_setxattr(struct inode *inode, const char *name,
do_sync:
spin_unlock(&ci->i_ceph_lock);
ceph_buffer_put(old_blob);
do_sync_unlocked:
if (lock_snap_rwsem)
up_read(&mdsc->snap_rwsem);

View File

@@ -115,6 +115,11 @@ static int __ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot,
if (ret)
return ret;
if (plaintext_len < sizeof(*hdr)) {
pr_err("%s plaintext too small %d\n", __func__, plaintext_len);
return -EINVAL;
}
hdr = p + ceph_crypt_data_offset(key);
if (le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC) {
pr_err("%s bad magic\n", __func__);

View File

@@ -47,7 +47,6 @@ int crush_get_bucket_item_weight(const struct crush_bucket *b, int p)
void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b)
{
kfree(b->h.items);
kfree(b);
}
void crush_destroy_bucket_list(struct crush_bucket_list *b)
@@ -55,14 +54,12 @@ void crush_destroy_bucket_list(struct crush_bucket_list *b)
kfree(b->item_weights);
kfree(b->sum_weights);
kfree(b->h.items);
kfree(b);
}
void crush_destroy_bucket_tree(struct crush_bucket_tree *b)
{
kfree(b->h.items);
kfree(b->node_weights);
kfree(b);
}
void crush_destroy_bucket_straw(struct crush_bucket_straw *b)
@@ -70,14 +67,12 @@ void crush_destroy_bucket_straw(struct crush_bucket_straw *b)
kfree(b->straws);
kfree(b->item_weights);
kfree(b->h.items);
kfree(b);
}
void crush_destroy_bucket_straw2(struct crush_bucket_straw2 *b)
{
kfree(b->item_weights);
kfree(b->h.items);
kfree(b);
}
void crush_destroy_bucket(struct crush_bucket *b)
@@ -99,6 +94,7 @@ void crush_destroy_bucket(struct crush_bucket *b)
crush_destroy_bucket_straw2((struct crush_bucket_straw2 *)b);
break;
}
kfree(b);
}
/**

View File

@@ -72,8 +72,7 @@ static int crush_decode_uniform_bucket(void **p, void *end,
struct crush_bucket_uniform *b)
{
dout("crush_decode_uniform_bucket %p to %p\n", *p, end);
ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad);
b->item_weight = ceph_decode_32(p);
ceph_decode_32_safe(p, end, b->item_weight, bad);
return 0;
bad:
return -EINVAL;
@@ -389,11 +388,15 @@ static int decode_choose_args(void **p, void *end, struct crush_map *c)
goto fail;
if (arg->ids_size &&
arg->ids_size != c->buckets[bucket_index]->size)
(!c->buckets[bucket_index] ||
arg->ids_size != c->buckets[bucket_index]->size))
goto e_inval;
}
insert_choose_arg_map(&c->choose_args, arg_map);
if (!__insert_choose_arg_map(&c->choose_args, arg_map)) {
ret = -EEXIST;
goto fail;
}
}
return 0;
@@ -516,6 +519,10 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
b->id = ceph_decode_32(p);
b->type = ceph_decode_16(p);
b->alg = ceph_decode_8(p);
if (b->alg != alg) {
b->alg = 0;
goto bad;
}
b->hash = ceph_decode_8(p);
b->weight = ceph_decode_32(p);
b->size = ceph_decode_32(p);
@@ -1702,7 +1709,7 @@ static int osdmap_decode(void **p, void *end, bool msgr2,
ceph_decode_need(p, end, 3*sizeof(u32) +
map->max_osd*(struct_v >= 5 ? sizeof(u32) :
sizeof(u8)) +
sizeof(*map->osd_weight), e_inval);
map->max_osd*sizeof(*map->osd_weight), e_inval);
if (ceph_decode_32(p) != map->max_osd)
goto e_inval;