From a354d7eaa1a57f1532c8072a424cc2d339a73cc0 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 14 Jul 2026 14:20:37 +0800 Subject: [PATCH] ceph: fix use-after-dereference of NULL ci in __ceph_remove_cap() The NULL check for "ci" in __ceph_remove_cap() was dead code because ci was dereferenced via &ci->netfs.inode before the check, and cap->session was dereferenced via session->s_mdsc->fsc->client even earlier. On a double-remove, both cap->ci and cap->session are set to NULL by the first call, so the second call would crash before ever reaching the guard. Move ci, session, cl, and inode initializations after the NULL check so that the early-return actually works. Signed-off-by: Xiubo Li Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 1e6ffe23fd08..f3110e8d19a8 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1129,18 +1129,21 @@ int ceph_is_any_caps(struct inode *inode) */ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) { - struct ceph_mds_session *session = cap->session; - struct ceph_client *cl = session->s_mdsc->fsc->client; - struct ceph_inode_info *ci = cap->ci; - struct inode *inode = &ci->netfs.inode; + struct ceph_mds_session *session; + struct ceph_client *cl; + struct ceph_inode_info *ci; + struct inode *inode; struct ceph_mds_client *mdsc; int removed = 0; /* 'ci' being NULL means the remove have already occurred */ - if (!ci) { - doutc(cl, "inode is NULL\n"); + ci = cap->ci; + if (!ci) return; - } + + session = cap->session; + cl = session->s_mdsc->fsc->client; + inode = &ci->netfs.inode; lockdep_assert_held(&ci->i_ceph_lock);