[PATCH] ceph: reject mdsmaps that claim more than CEPH_MAX_MDS ranks

From: Xiang Mei

Date: Tue Sep 15 2026 - 01:21:19 EST


check_new_map() sizes its export-target bitmap from CEPH_MAX_MDS but walks
it with newmap->possible_max_rank, which comes straight off the wire:
ceph_mdsmap_decode() sets it to max(m_num_active_mds, m_max_mds), then
overwrites it with the size of the "in" set. Neither is bounded by
CEPH_MAX_MDS, only by m_info allocating, so a monitor announcing more ranks
than the bitmap holds makes test_bit() read past the end of targets[]. The
clear_bit() loop above indexes the same bitmap, though it is additionally
bounded by mdsc->max_sessions. The map arrives on the monitor connection,
which is unauthenticated in legacy msgr mode.

CEPH_MAX_MDS is a protocol constant from src/mds/mdstypes.h, and the
reserved inode ranges in super.h are laid out one rank apart over that
span, so a map claiming more ranks is corrupt. Reject it where the rank
space is established rather than letting the count reach check_new_map().

BUG: KASAN: stack-out-of-bounds in check_new_map (fs/ceph/mds_client.c:5968)
Read of size 8 at addr ffffc90000ac7798 by task kworker/1:2/127
Workqueue: ceph-msgr ceph_con_workfn
Call Trace:
<TASK>
dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
print_report (mm/kasan/report.c:378 mm/kasan/report.c:482)
kasan_report (mm/kasan/report.c:595)
kasan_check_range (mm/kasan/generic.c:186 mm/kasan/generic.c:200)
check_new_map (fs/ceph/mds_client.c:5968)
ceph_mdsc_handle_mdsmap (fs/ceph/mds_client.c:7060)
extra_mon_dispatch (fs/ceph/super.c:802)
mon_dispatch (net/ceph/mon_client.c:1483)
ceph_con_process_message (net/ceph/messenger.c:1424)
ceph_con_v1_try_read (net/ceph/messenger_v1.c:1430)
ceph_con_workfn (net/ceph/messenger.c:1576)
process_one_work (kernel/workqueue.c:3396)
worker_thread (kernel/workqueue.c:3479 kernel/workqueue.c:3560)
kthread (kernel/kthread.c:436)
ret_from_fork (arch/x86/kernel/process.c:158)
ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
</TASK>
The buggy address belongs to stack of task kworker/1:2/127
and is located at offset 288 in frame:
check_new_map (fs/ceph/mds_client.c:5846)
This frame has 1 object:
[32, 288) 'targets'

Cc: stable@xxxxxxxxxxxxxxx
Fixes: d517b3983dd3 ("ceph: reconnect to the export targets on new mdsmaps")
Reported-by: <co+f8e3e01d8dda8689@xxxxxxx>
Assisted-by: LLM
Signed-off-by: Xiang Mei <xmei5@xxxxxxx>
---
fs/ceph/mdsmap.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index 53079ef34c3a..5e3b8c3ca502 100644
--- a/fs/ceph/mdsmap.c
+++ b/fs/ceph/mdsmap.c
@@ -170,6 +170,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
* and the mds rank >= m_num_active_mds.
*/
m->possible_max_rank = max(m->m_num_active_mds, m->m_max_mds);
+ if (m->possible_max_rank > CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }

m->m_info = kzalloc_objs(*m->m_info, m->possible_max_rank, GFP_NOFS);
if (!m->m_info)
@@ -316,6 +320,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
{
int num_laggy = 0;
ceph_decode_32_safe(p, end, n, bad_ext);
+ if (n > CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }
ceph_decode_need(p, end, sizeof(u32) * n, bad_ext);

for (i = 0; i < n; i++) {
--
2.43.0