Unverified Commit cdcacb47 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!8725 v6 Fix CVE-2023-52732

Merge Pull Request from: @ci-robot 
 
PR sync from: Zizhi Wo <wozizhi@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/LV47KDPWILFGIDXQLJIRCKVPEFGIRW76/ 
V6: Fix missing adaptations in ceph_do_readpage() and ceph_readpages().

V5: Fix wrong adaptations in ceph_readpage_to_fscache() and
ceph_readpage_ceph_readpage_from_fscache().

V4: Fix wrong adaptation in ceph_mdsc_sync().

V3: Fix wrong adaptation in remove_session_caps_cb().

V2: Modify conflicting files in the commit message.

V1: Fix main error.

Jeff Layton (4):
  ceph: drop private list from remove_session_caps_cb
  ceph: fix auth cap handling logic in remove_session_caps_cb
  ceph: refactor remove_session_caps_cb
  ceph: shut down access to inode when async create fails

Xiubo Li (1):
  ceph: blocklist the kclient when receiving corrupted snap trace


-- 
2.39.2
 
https://gitee.com/src-openeuler/kernel/issues/I9R4KH 
 
Link:https://gitee.com/openeuler/kernel/pulls/8725

 

Reviewed-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents 44f6f535 4925e679
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -187,6 +187,9 @@ static int ceph_do_readpage(struct file *filp, struct page *page)
	u64 off = page_offset(page);
	u64 len = PAGE_SIZE;

	if (ceph_inode_is_shutdown(inode))
		return -EIO;

	if (off >= i_size_read(inode)) {
		zero_user_segment(page, 0, PAGE_SIZE);
		SetPageUptodate(page);
@@ -460,6 +463,9 @@ static int ceph_readpages(struct file *file, struct address_space *mapping,
	int rc = 0;
	int max = 0;

	if (ceph_inode_is_shutdown(inode))
		return -EIO;

	if (ceph_inode(inode)->i_inline_version != CEPH_INLINE_NONE)
		return -EINVAL;

@@ -603,6 +609,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)

	dout("writepage %p idx %lu\n", page, page->index);

	if (ceph_inode_is_shutdown(inode))
		return -EIO;

	/* verify this is a writeable snap context */
	snapc = page_snap_context(page);
	if (!snapc) {
@@ -832,7 +841,7 @@ static int ceph_writepages_start(struct address_space *mapping,
	     wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
	     (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD"));

	if (READ_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) {
	if (ceph_inode_is_shutdown(inode)) {
		if (ci->i_wrbuffer_ref > 0) {
			pr_warn_ratelimited(
				"writepage_start %p %lld forced umount\n",
@@ -1253,12 +1262,12 @@ static struct ceph_snap_context *
ceph_find_incompatible(struct page *page)
{
	struct inode *inode = page->mapping->host;
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
	struct ceph_inode_info *ci = ceph_inode(inode);

	if (READ_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) {
		dout(" page %p forced umount\n", page);
		return ERR_PTR(-EIO);
	if (ceph_inode_is_shutdown(inode)) {
		dout(" page %p %llx:%llx is shutdown\n", page,
		     ceph_vinop(inode));
		return ERR_PTR(-ESTALE);
	}

	for (;;) {
@@ -1496,6 +1505,9 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf)
	sigset_t oldset;
	vm_fault_t ret = VM_FAULT_SIGBUS;

	if (ceph_inode_is_shutdown(inode))
		return ret;

	ceph_block_sigs(&oldset);

	dout("filemap_fault %p %llx.%llx %llu~%zd trying to get caps\n",
@@ -1591,6 +1603,9 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)
	sigset_t oldset;
	vm_fault_t ret = VM_FAULT_SIGBUS;

	if (ceph_inode_is_shutdown(inode))
		return ret;

	prealloc_cf = ceph_alloc_cap_flush();
	if (!prealloc_cf)
		return VM_FAULT_OOM;
@@ -1754,6 +1769,11 @@ int ceph_uninline_data(struct file *filp, struct page *locked_page)
	dout("uninline_data %p %llx.%llx inline_version %llu\n",
	     inode, ceph_vinop(inode), inline_version);

	if (ceph_inode_is_shutdown(inode)) {
		err = -EIO;
		goto out;
	}

	if (inline_version == 1 || /* initial version, no data */
	    inline_version == CEPH_INLINE_NONE)
		goto out;
+132 −6
Original line number Diff line number Diff line
@@ -2839,9 +2839,9 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
			goto out_unlock;
		}

		if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) {
			dout("get_cap_refs %p forced umount\n", inode);
			ret = -EIO;
		if (ceph_inode_is_shutdown(inode)) {
			dout("get_cap_refs %p inode is shutdown\n", inode);
			ret = -ESTALE;
			goto out_unlock;
		}
		mds_wanted = __ceph_caps_mds_wanted(ci, false);
@@ -4109,6 +4109,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
	void *p, *end;
	struct cap_extra_info extra_info = {};
	bool queue_trunc;
	bool close_sessions = false;

	dout("handle_caps from mds%d\n", session->s_mds);

@@ -4249,9 +4250,13 @@ void ceph_handle_caps(struct ceph_mds_session *session,
		realm = NULL;
		if (snaptrace_len) {
			down_write(&mdsc->snap_rwsem);
			ceph_update_snap_trace(mdsc, snaptrace,
			if (ceph_update_snap_trace(mdsc, snaptrace,
						   snaptrace + snaptrace_len,
					       false, &realm);
						   false, &realm)) {
				up_write(&mdsc->snap_rwsem);
				close_sessions = true;
				goto done;
			}
			downgrade_write(&mdsc->snap_rwsem);
		} else {
			down_read(&mdsc->snap_rwsem);
@@ -4311,6 +4316,11 @@ void ceph_handle_caps(struct ceph_mds_session *session,
	ceph_put_string(extra_info.pool_ns);
	/* avoid calling iput_final() in mds dispatch threads */
	ceph_async_iput(inode);

	/* Defer closing the sessions after s_mutex lock being released */
	if (close_sessions)
		ceph_mdsc_close_sessions(mdsc);

	return;

flush_cap_releases:
@@ -4633,3 +4643,119 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry,
	spin_unlock(&dentry->d_lock);
	return ret;
}

static int remove_capsnaps(struct ceph_mds_client *mdsc, struct inode *inode)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct ceph_cap_snap *capsnap;
	int capsnap_release = 0;

	lockdep_assert_held(&ci->i_ceph_lock);

	dout("removing capsnaps, ci is %p, inode is %p\n", ci, inode);

	while (!list_empty(&ci->i_cap_snaps)) {
		capsnap = list_first_entry(&ci->i_cap_snaps,
					   struct ceph_cap_snap, ci_item);
		__ceph_remove_capsnap(inode, capsnap, NULL, NULL);
		ceph_put_snap_context(capsnap->context);
		ceph_put_cap_snap(capsnap);
		capsnap_release++;
	}
	wake_up_all(&ci->i_cap_wq);
	wake_up_all(&mdsc->cap_flushing_wq);
	return capsnap_release;
}

int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invalidate)
{
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
	struct ceph_mds_client *mdsc = fsc->mdsc;
	struct ceph_inode_info *ci = ceph_inode(inode);
	bool is_auth;
	bool dirty_dropped = false;
	int iputs = 0;

	lockdep_assert_held(&ci->i_ceph_lock);

	dout("removing cap %p, ci is %p, inode is %p\n",
	     cap, ci, &ci->vfs_inode);

	is_auth = (cap == ci->i_auth_cap);
	__ceph_remove_cap(cap, false);
	if (is_auth) {
		struct ceph_cap_flush *cf;

		if (ceph_inode_is_shutdown(inode)) {
			if (inode->i_data.nrpages > 0)
				*invalidate = true;
			if (ci->i_wrbuffer_ref > 0)
				mapping_set_error(&inode->i_data, -EIO);
		}

		spin_lock(&mdsc->cap_dirty_lock);

		/* trash all of the cap flushes for this inode */
		while (!list_empty(&ci->i_cap_flush_list)) {
			cf = list_first_entry(&ci->i_cap_flush_list,
					      struct ceph_cap_flush, i_list);
			list_del_init(&cf->g_list);
			list_del_init(&cf->i_list);
			if (!cf->is_capsnap)
				ceph_free_cap_flush(cf);
		}

		if (!list_empty(&ci->i_dirty_item)) {
			pr_warn_ratelimited(
				" dropping dirty %s state for %p %lld\n",
				ceph_cap_string(ci->i_dirty_caps),
				inode, ceph_ino(inode));
			ci->i_dirty_caps = 0;
			list_del_init(&ci->i_dirty_item);
			dirty_dropped = true;
		}
		if (!list_empty(&ci->i_flushing_item)) {
			pr_warn_ratelimited(
				" dropping dirty+flushing %s state for %p %lld\n",
				ceph_cap_string(ci->i_flushing_caps),
				inode, ceph_ino(inode));
			ci->i_flushing_caps = 0;
			list_del_init(&ci->i_flushing_item);
			mdsc->num_cap_flushing--;
			dirty_dropped = true;
		}
		spin_unlock(&mdsc->cap_dirty_lock);

		if (dirty_dropped) {
			mapping_set_error(inode->i_mapping, -EIO);

			if (ci->i_wrbuffer_ref_head == 0 &&
			    ci->i_wr_ref == 0 &&
			    ci->i_dirty_caps == 0 &&
			    ci->i_flushing_caps == 0) {
				ceph_put_snap_context(ci->i_head_snapc);
				ci->i_head_snapc = NULL;
			}
		}

		if (atomic_read(&ci->i_filelock_ref) > 0) {
			/* make further file lock syscall return -EIO */
			ci->i_ceph_flags |= CEPH_I_ERROR_FILELOCK;
			pr_warn_ratelimited(" dropping file locks for %p %lld\n",
					    inode, ceph_ino(inode));
		}

		if (!ci->i_dirty_caps && ci->i_prealloc_cap_flush) {
			cf = ci->i_prealloc_cap_flush;
			ci->i_prealloc_cap_flush = NULL;
			if (!cf->is_capsnap)
				ceph_free_cap_flush(cf);
		}

		if (!list_empty(&ci->i_cap_snaps))
			iputs = remove_capsnaps(mdsc, inode);
	}
	if (dirty_dropped)
		++iputs;
	return iputs;
}
+11 −1
Original line number Diff line number Diff line
@@ -157,6 +157,11 @@ static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
		ceph_mdsc_put_request(req);
		if (!inode)
			return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
	} else {
		if (ceph_inode_is_shutdown(inode)) {
			iput(inode);
			return ERR_PTR(-ESTALE);
		}
	}
	return inode;
}
@@ -223,8 +228,13 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
		return ERR_PTR(-ESTALE);

	inode = ceph_find_inode(sb, vino);
	if (inode)
	if (inode) {
		if (ceph_inode_is_shutdown(inode)) {
			iput(inode);
			return ERR_PTR(-ESTALE);
		}
		return d_obtain_alias(inode);
	}

	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
				       USE_ANY_MDS);
+12 −1
Original line number Diff line number Diff line
@@ -525,6 +525,7 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,

	if (result) {
		struct dentry *dentry = req->r_dentry;
		struct inode *inode = d_inode(dentry);
		int pathlen = 0;
		u64 base = 0;
		char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
@@ -534,7 +535,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
		if (!d_unhashed(dentry))
			d_drop(dentry);

		/* FIXME: start returning I/O errors on all accesses? */
		ceph_inode_shutdown(inode);

		pr_warn("ceph: async create failure path=(%llx)%s result=%d!\n",
			base, IS_ERR(path) ? "<<bad>>" : path, result);
		ceph_mdsc_free_path(path, pathlen);
@@ -1557,6 +1559,9 @@ static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to)
	dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n",
	     inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len, inode);

	if (ceph_inode_is_shutdown(inode))
		return -ESTALE;

	if (direct_lock)
		ceph_start_io_direct(inode);
	else
@@ -1714,6 +1719,9 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
	loff_t pos;
	loff_t limit = max(i_size_read(inode), fsc->max_file_size);

	if (ceph_inode_is_shutdown(inode))
		return -ESTALE;

	if (ceph_snap(inode) != CEPH_NOSNAP)
		return -EROFS;

@@ -1996,6 +2004,9 @@ static int ceph_zero_partial_object(struct inode *inode,
	loff_t zero = 0;
	int op;

	if (ceph_inode_is_shutdown(inode))
		return -EIO;

	if (!length) {
		op = offset ? CEPH_OSD_OP_DELETE : CEPH_OSD_OP_TRUNCATE;
		length = &zero;
+31 −2
Original line number Diff line number Diff line
@@ -1884,13 +1884,12 @@ void ceph_queue_vmtruncate(struct inode *inode)
static void ceph_do_invalidate_pages(struct inode *inode)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
	u32 orig_gen;
	int check = 0;

	mutex_lock(&ci->i_truncate_mutex);

	if (READ_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) {
	if (ceph_inode_is_shutdown(inode)) {
		pr_warn_ratelimited("invalidate_pages %p %lld forced umount\n",
				    inode, ceph_ino(inode));
		mapping_set_error(inode->i_mapping, -EIO);
@@ -2254,6 +2253,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
	if (ceph_snap(inode) != CEPH_NOSNAP)
		return -EROFS;

	if (ceph_inode_is_shutdown(inode))
		return -ESTALE;

	err = setattr_prepare(dentry, attr);
	if (err != 0)
		return err;
@@ -2375,6 +2377,9 @@ int ceph_getattr(const struct path *path, struct kstat *stat,
	u32 valid_mask = STATX_BASIC_STATS;
	int err = 0;

	if (ceph_inode_is_shutdown(inode))
		return -ESTALE;

	/* Skip the getattr altogether if we're asked not to sync */
	if (!(flags & AT_STATX_DONT_SYNC)) {
		err = ceph_do_getattr(inode, statx_to_caps(request_mask),
@@ -2421,3 +2426,27 @@ int ceph_getattr(const struct path *path, struct kstat *stat,
	stat->result_mask = request_mask & valid_mask;
	return err;
}

void ceph_inode_shutdown(struct inode *inode)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct rb_node *p;
	int iputs = 0;
	bool invalidate = false;

	spin_lock(&ci->i_ceph_lock);
	ci->i_ceph_flags |= CEPH_I_SHUTDOWN;
	p = rb_first(&ci->i_caps);
	while (p) {
		struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node);

		p = rb_next(p);
		iputs += ceph_purge_inode_cap(inode, cap, &invalidate);
	}
	spin_unlock(&ci->i_ceph_lock);

	if (invalidate)
		ceph_queue_invalidate(inode);
	while (iputs--)
		iput(inode);
}
Loading