[PATCH] ceph: fix use-after-dereference of NULL ci in __ceph_remove_cap()
From: Xiubo Li via B4 Relay
Date: Tue Jul 14 2026 - 02:22:31 EST
From: Xiubo Li <xiubo.li@xxxxxxxxx>
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 <xiubo.li@xxxxxxxxx>
---
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 <xiubo.li@xxxxxxxxx>