mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
A bunch of assorted fixes with the majority being hardening against
malformed input and invalid data scenarios that don't happen in real deployments but can be utilized to trigger use-after-free and similar issues, some error path leak fixups and two patches from Max to avoid a potential hang in __ceph_get_caps() and unintended nesting of current->journal_info while handling replies from the MDS. All marked for stable. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAmpjx6wTHGlkcnlvbW92 QGdtYWlsLmNvbQAKCRBKf944AhHzixDmB/4/J+dqhhDKg69t0ADnAPLgxe3AoXwi 7GRd2Uk5/AU9o+1fexyfWt2u+3ZtAVGLVRZPF1ARFQQDGUoj99X06NlTyVAFRNMb p78Sun5iuiDJb72UOD3WzW9lmpjSeCVUbTuTadtmF4y34KIvZ7AYltjZBpycE2Yj lyiUr4CSkXMAa/wWDKg+8SAw2tBI61WdyJeyu8ESCUmm9Q2XLq1+2N/jhfifk0oC Dc7EHUZmg7YNbTr0DKyLXdAoZHE54zKC2qDrvfTPmpTsOhMI2v5LY6JtXzm8/l6c wAGgIj5hRePGDDPb1V5Mbz3rkY/VIGdKmkqEV4PZn+IHafZGp45lnS7V =q2wM -----END PGP SIGNATURE----- Merge tag 'ceph-for-7.2-rc5' of https://github.com/ceph/ceph-client Pull ceph fixes from Ilya Dryomov: "A bunch of assorted fixes with the majority being hardening against malformed input and invalid data scenarios that don't happen in real deployments but can be utilized to trigger use-after-free and similar issues, some error path leak fixups and two patches from Max to avoid a potential hang in __ceph_get_caps() and unintended nesting of current->journal_info while handling replies from the MDS. All marked for stable" * tag 'ceph-for-7.2-rc5' of https://github.com/ceph/ceph-client: ceph: avoid fs reclaim while using current->journal_info ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT* ceph: fix hanging __ceph_get_caps() with stale mds_wanted rbd: Reset positive result codes to zero in object map update path libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE libceph: refresh auth->authorizer_buf{,_len} after authorizer update ceph: fix refcount leak in ceph_readdir() libceph: guard missing CRUSH type name lookup libceph: remove debugfs files before client teardown libceph: bound get_version reply decode to front len ceph: fix writeback_count leak in write_folio_nounlock() libceph: fix two unsafe bare decodes in decode_lockers() ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps() libceph: Reject monmaps advertising zero monitors libceph: reject zero bucket types in crush_decode libceph: Fix multiplication overflow in decode_new_up_state_weight()
This commit is contained in:
commit
dad0a87d79
|
|
@ -1950,9 +1950,14 @@ static int rbd_object_map_update_finish(struct rbd_obj_request *obj_req,
|
|||
bool has_current_state;
|
||||
void *p;
|
||||
|
||||
if (osd_req->r_result)
|
||||
if (osd_req->r_result < 0)
|
||||
return osd_req->r_result;
|
||||
|
||||
/*
|
||||
* Writes aren't allowed to return a data payload.
|
||||
*/
|
||||
WARN_ON_ONCE(osd_req->r_result > 0);
|
||||
|
||||
/*
|
||||
* Nothing to do for a snapshot object map.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -790,6 +790,9 @@ static int write_folio_nounlock(struct folio *folio,
|
|||
ceph_wbc.truncate_size, true);
|
||||
if (IS_ERR(req)) {
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
if (atomic_long_dec_return(&fsc->writeback_count) <
|
||||
CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
|
||||
fsc->write_congested = false;
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
|
|
@ -809,6 +812,9 @@ static int write_folio_nounlock(struct folio *folio,
|
|||
folio_redirty_for_writepage(wbc, folio);
|
||||
folio_end_writeback(folio);
|
||||
ceph_osdc_put_request(req);
|
||||
if (atomic_long_dec_return(&fsc->writeback_count) <
|
||||
CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
|
||||
fsc->write_congested = false;
|
||||
return PTR_ERR(bounce_page);
|
||||
}
|
||||
}
|
||||
|
|
@ -847,6 +853,9 @@ static int write_folio_nounlock(struct folio *folio,
|
|||
ceph_vinop(inode), folio);
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
folio_end_writeback(folio);
|
||||
if (atomic_long_dec_return(&fsc->writeback_count) <
|
||||
CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
|
||||
fsc->write_congested = false;
|
||||
return err;
|
||||
}
|
||||
if (err == -EBLOCKLISTED)
|
||||
|
|
|
|||
|
|
@ -3094,7 +3094,19 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need,
|
|||
ret = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
|
||||
|
||||
/*
|
||||
* If a cap update is lost after
|
||||
* mds_wanted was raised, waiting
|
||||
* forever will never make progress.
|
||||
* Retry the renew path periodically
|
||||
* so we can resend synchronously.
|
||||
*/
|
||||
if (!wait_woken(&wait, TASK_INTERRUPTIBLE,
|
||||
CEPH_GET_CAPS_WAIT_TIMEOUT)) {
|
||||
ret = -EUCLEAN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
remove_wait_queue(&ci->i_cap_wq, &wait);
|
||||
|
|
@ -3128,7 +3140,8 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need,
|
|||
continue;
|
||||
}
|
||||
if (ret == -EUCLEAN) {
|
||||
/* session was killed, try renew caps */
|
||||
/* session was killed or a waited cap
|
||||
* request needs a retry */
|
||||
ret = ceph_renew_caps(inode, flags);
|
||||
if (ret == 0)
|
||||
continue;
|
||||
|
|
@ -4375,6 +4388,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
|
|||
|
||||
snaptrace = h + 1;
|
||||
snaptrace_len = le32_to_cpu(h->snap_trace_len);
|
||||
ceph_decode_need(&snaptrace, end, snaptrace_len, bad);
|
||||
p = snaptrace + snaptrace_len;
|
||||
|
||||
if (msg_version >= 2) {
|
||||
|
|
|
|||
|
|
@ -546,11 +546,16 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
|
|||
pr_warn_client(cl,
|
||||
"%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n",
|
||||
inode, ceph_vinop(inode), rde->offset, ctx->pos);
|
||||
ceph_mdsc_put_request(dfi->last_readdir);
|
||||
dfi->last_readdir = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!rde->inode.in))
|
||||
if (WARN_ON_ONCE(!rde->inode.in)) {
|
||||
ceph_mdsc_put_request(dfi->last_readdir);
|
||||
dfi->last_readdir = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ctx->pos = rde->offset;
|
||||
doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode,
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
|
|||
}
|
||||
|
||||
/*
|
||||
* try renew caps after session gets killed.
|
||||
* Retry cap acquisition after a stale session or a lost cap update.
|
||||
*/
|
||||
int ceph_renew_caps(struct inode *inode, int fmode)
|
||||
{
|
||||
|
|
@ -322,14 +322,15 @@ int ceph_renew_caps(struct inode *inode, int fmode)
|
|||
struct ceph_client *cl = mdsc->fsc->client;
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_mds_request *req;
|
||||
int err, flags, wanted;
|
||||
int err, flags, wanted, issued;
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
__ceph_touch_fmode(ci, mdsc, fmode);
|
||||
wanted = __ceph_caps_file_wanted(ci);
|
||||
issued = __ceph_caps_issued(ci, NULL);
|
||||
if (__ceph_is_any_real_caps(ci) &&
|
||||
(!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) {
|
||||
int issued = __ceph_caps_issued(ci, NULL);
|
||||
(!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap) &&
|
||||
(issued & wanted) == wanted) {
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n",
|
||||
inode, ceph_vinop(inode), ceph_cap_string(wanted),
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
|
|||
struct ceph_ioctl_layout nl;
|
||||
int err;
|
||||
|
||||
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
|
||||
return -EACCES;
|
||||
|
||||
if (copy_from_user(&l, arg, sizeof(l)))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg)
|
|||
int err;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;
|
||||
|
||||
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
|
||||
return -EACCES;
|
||||
|
||||
/* copy and validate */
|
||||
if (copy_from_user(&l, arg, sizeof(l)))
|
||||
return -EFAULT;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
@ -4015,6 +4016,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
|
|||
struct ceph_mds_reply_head *head = msg->front.iov_base;
|
||||
struct ceph_mds_reply_info_parsed *rinfo; /* parsed reply info */
|
||||
struct ceph_snap_realm *realm;
|
||||
unsigned int nofs_flags;
|
||||
u64 tid;
|
||||
int err, result;
|
||||
int mds = session->s_mds;
|
||||
|
|
@ -4158,6 +4160,14 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
|
|||
|
||||
/* insert trace into our cache */
|
||||
mutex_lock(&req->r_fill_mutex);
|
||||
|
||||
/* disable fs reclaim while we are using current->journal_info
|
||||
* for our own purposes, or else shrinkers of other
|
||||
* filesystems might dereference this pointer as a different
|
||||
* type
|
||||
*/
|
||||
nofs_flags = memalloc_nofs_save();
|
||||
|
||||
current->journal_info = req;
|
||||
err = ceph_fill_trace(mdsc->fsc->sb, req);
|
||||
if (err == 0) {
|
||||
|
|
@ -4166,6 +4176,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
|
|||
err = ceph_readdir_prepopulate(req, req->r_session);
|
||||
}
|
||||
current->journal_info = NULL;
|
||||
memalloc_nofs_restore(nofs_flags);
|
||||
mutex_unlock(&req->r_fill_mutex);
|
||||
|
||||
up_read(&mdsc->snap_rwsem);
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ struct ceph_fs_client;
|
|||
struct ceph_cap;
|
||||
|
||||
#define MDS_AUTH_UID_ANY -1
|
||||
#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ)
|
||||
#define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60
|
||||
#define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5
|
||||
#define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5
|
||||
|
|
|
|||
|
|
@ -849,9 +849,16 @@ static int ceph_x_update_authorizer(
|
|||
|
||||
au = (struct ceph_x_authorizer *)auth->authorizer;
|
||||
if (au->secret_id < th->secret_id) {
|
||||
int ret;
|
||||
|
||||
dout("ceph_x_update_authorizer service %u secret %llu < %llu\n",
|
||||
au->service, au->secret_id, th->secret_id);
|
||||
return ceph_x_build_authorizer(ac, th, au);
|
||||
ret = ceph_x_build_authorizer(ac, th, au);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
auth->authorizer_buf = au->buf->vec.iov_base;
|
||||
auth->authorizer_buf_len = au->buf->vec.iov_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -762,13 +762,13 @@ void ceph_destroy_client(struct ceph_client *client)
|
|||
|
||||
atomic_set(&client->msgr.stopping, 1);
|
||||
|
||||
ceph_debugfs_client_cleanup(client);
|
||||
|
||||
/* unmount */
|
||||
ceph_osdc_stop(&client->osdc);
|
||||
ceph_monc_stop(&client->monc);
|
||||
ceph_messenger_fini(&client->msgr);
|
||||
|
||||
ceph_debugfs_client_cleanup(client);
|
||||
|
||||
ceph_destroy_options(client->options);
|
||||
|
||||
kfree(client);
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*num_lockers = ceph_decode_32(p);
|
||||
ceph_decode_32_safe(p, end, *num_lockers, err_inval);
|
||||
*lockers = kzalloc_objs(**lockers, *num_lockers, GFP_NOIO);
|
||||
if (!*lockers)
|
||||
return -ENOMEM;
|
||||
|
|
@ -310,7 +310,8 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
|
|||
goto err_free_lockers;
|
||||
}
|
||||
|
||||
*type = ceph_decode_8(p);
|
||||
ret = -EINVAL;
|
||||
ceph_decode_8_safe(p, end, *type, err_free_lockers);
|
||||
s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO);
|
||||
if (IS_ERR(s)) {
|
||||
ret = PTR_ERR(s);
|
||||
|
|
@ -320,6 +321,9 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
|
|||
*tag = s;
|
||||
return 0;
|
||||
|
||||
err_inval:
|
||||
return -EINVAL;
|
||||
|
||||
err_free_lockers:
|
||||
ceph_free_lockers(*lockers, *num_lockers);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_decode(void **p, void *end, bool msgr2)
|
|||
|
||||
dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch,
|
||||
num_mon);
|
||||
if (num_mon > CEPH_MAX_MON)
|
||||
if (num_mon == 0 || num_mon > CEPH_MAX_MON)
|
||||
goto e_inval;
|
||||
|
||||
monmap = kmalloc_flex(*monmap, mon_inst, num_mon, GFP_NOIO);
|
||||
|
|
@ -821,7 +821,7 @@ static void handle_get_version_reply(struct ceph_mon_client *monc,
|
|||
struct ceph_mon_generic_request *req;
|
||||
u64 tid = le64_to_cpu(msg->hdr.tid);
|
||||
void *p = msg->front.iov_base;
|
||||
void *end = p + msg->front_alloc_len;
|
||||
void *const end = p + msg->front.iov_len;
|
||||
u64 handle;
|
||||
|
||||
dout("%s msg %p tid %llu\n", __func__, msg, tid);
|
||||
|
|
|
|||
|
|
@ -518,6 +518,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
|
|||
ceph_decode_need(p, end, 4*sizeof(u32), bad);
|
||||
b->id = ceph_decode_32(p);
|
||||
b->type = ceph_decode_16(p);
|
||||
if (b->type == 0)
|
||||
goto bad;
|
||||
b->alg = ceph_decode_8(p);
|
||||
if (b->alg != alg) {
|
||||
b->alg = 0;
|
||||
|
|
@ -1436,7 +1438,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end,
|
|||
ceph_decode_32_safe(p, end, len, e_inval);
|
||||
if (len == 0 && incremental)
|
||||
return NULL; /* new_pg_temp: [] to remove */
|
||||
if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
|
||||
if (len > CEPH_PG_MAX_SIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ceph_decode_need(p, end, len * sizeof(u32), e_inval);
|
||||
|
|
@ -1617,7 +1619,7 @@ static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end,
|
|||
u32 len, i;
|
||||
|
||||
ceph_decode_32_safe(p, end, len, e_inval);
|
||||
if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
|
||||
if (len > CEPH_PG_MAX_SIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval);
|
||||
|
|
@ -1842,6 +1844,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v,
|
|||
void *new_up_client;
|
||||
void *new_state;
|
||||
void *new_weight_end;
|
||||
const u32 new_state_item_size =
|
||||
sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
|
||||
u32 len;
|
||||
int ret;
|
||||
int i;
|
||||
|
|
@ -1862,7 +1866,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v,
|
|||
|
||||
new_state = *p;
|
||||
ceph_decode_32_safe(p, end, len, e_inval);
|
||||
len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
|
||||
if (check_mul_overflow(len, new_state_item_size, &len))
|
||||
goto e_inval;
|
||||
ceph_decode_need(p, end, len, e_inval);
|
||||
*p += len;
|
||||
|
||||
|
|
@ -3055,8 +3060,11 @@ static int get_immediate_parent(struct crush_map *c, int id,
|
|||
if (b->items[j] != id)
|
||||
continue;
|
||||
|
||||
*parent_type_id = b->type;
|
||||
type_cn = lookup_crush_name(&c->type_names, b->type);
|
||||
if (WARN_ON_ONCE(!type_cn))
|
||||
continue;
|
||||
|
||||
*parent_type_id = b->type;
|
||||
parent_loc->cl_type_name = type_cn->cn_name;
|
||||
parent_loc->cl_name = cn->cn_name;
|
||||
return b->id;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user