[PATCH] ceph: fix use-after-dereference of NULL ci in __ceph_remove_cap()

Xiubo Li via B4 Relay <[email protected]> Tue, 14 Jul 2026 14:20:37 +0800
Newsgroups org.kernel.vger.ceph-devel,org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Xiubo Li <[email protected]>

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 <[email protected]>
---
 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 f8d898ad091e..8568edf494b5 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -1154,18 +1154,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);
 

---
base-commit: fc67edb66b3c9924c4e0bb366a92b32ea13c526a
change-id: 20260714-ceph-fix-remove-cap-cf7d8a7a242a

Best regards,
--  
Xiubo Li <[email protected]>