[PATCH v4 2/3] ceph: stop checking a stale MDS map after dropping mutex
From: Max Kellermann
Date: Fri Sep 04 2026 - 11:37:19 EST
check_new_map() drops `mdsc->mutex` in several slow paths. Another
map handler can install a newer map and destroy the map being checked
before the original invocation re-locks the mutex. Continuing to
dereference newmap after that results in a use-after-free.
Use the `mdsmap->m_epoch` field to identify stale maps.
Fixes: 2f2dc053404f ("ceph: MDS client")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Max Kellermann <max.kellermann@xxxxxxxxx>
---
fs/ceph/mds_client.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index d36a114747ae..86d592f06196 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -5844,6 +5844,7 @@ static void check_new_map(struct ceph_mds_client *mdsc,
struct ceph_mdsmap *newmap,
struct ceph_mdsmap *oldmap)
{
+ u32 map_epoch = newmap->m_epoch;
int i, j, err;
int oldstate, newstate;
struct ceph_mds_session *s;
@@ -5887,6 +5888,8 @@ static void check_new_map(struct ceph_mds_client *mdsc,
ceph_put_mds_session(s);
mutex_lock(&mdsc->mutex);
+ if (mdsc->mdsmap->m_epoch != map_epoch)
+ return;
kick_requests(mdsc, i);
continue;
}
@@ -5898,6 +5901,11 @@ static void check_new_map(struct ceph_mds_client *mdsc,
mutex_unlock(&mdsc->mutex);
mutex_lock(&s->s_mutex);
mutex_lock(&mdsc->mutex);
+ if (mdsc->mdsmap->m_epoch != map_epoch) {
+ mutex_unlock(&s->s_mutex);
+ ceph_put_mds_session(s);
+ return;
+ }
ceph_con_close(&s->s_con);
mutex_unlock(&s->s_mutex);
s->s_state = CEPH_MDS_SESSION_RESTARTING;
@@ -5921,6 +5929,10 @@ static void check_new_map(struct ceph_mds_client *mdsc,
"mds%d reconnect failed: %d\n",
i, rc);
mutex_lock(&mdsc->mutex);
+ if (mdsc->mdsmap->m_epoch != map_epoch) {
+ ceph_put_mds_session(s);
+ return;
+ }
}
/*
@@ -5936,6 +5948,11 @@ static void check_new_map(struct ceph_mds_client *mdsc,
mutex_unlock(&mdsc->mutex);
mutex_lock(&s->s_mutex);
mutex_lock(&mdsc->mutex);
+ if (mdsc->mdsmap->m_epoch != map_epoch) {
+ mutex_unlock(&s->s_mutex);
+ ceph_put_mds_session(s);
+ return;
+ }
ceph_kick_flushing_caps(mdsc, s);
mutex_unlock(&s->s_mutex);
wake_up_session_caps(s, RECONNECT);
@@ -5991,6 +6008,8 @@ static void check_new_map(struct ceph_mds_client *mdsc,
i, err);
ceph_put_mds_session(s);
mutex_lock(&mdsc->mutex);
+ if (mdsc->mdsmap->m_epoch != map_epoch)
+ return;
}
for (i = 0; i < newmap->possible_max_rank && i < mdsc->max_sessions; i++) {
--
2.47.3