Re: [PATCH 3/4] ceph: bound num_export_targets array for mds info v2/v3

From: Viacheslav Dubeyko

Date: Thu Jun 04 2026 - 16:07:31 EST


On Thu, 2026-06-04 at 14:08 -0400, Michael Bommarito wrote:
> ceph_mdsmap_decode() in fs/ceph/mdsmap.c reads num_export_targets
> from
> each per-mds info record and advances the decode cursor by
> num_export_targets * sizeof(u32) without first checking that many
> bytes
> remain. The only upper-bound check that catches a runaway cursor
> (*p > info_end) is gated on info_v >= 4, because info_end is left
> NULL
> for info_v 2 and 3. When the monitor sends an MDS map whose per-mds
> info version is 2 or 3 with an oversized num_export_targets, the
> cursor
> moves past the message front buffer and the later export-targets loop
> calls the unchecked ceph_decode_32() on out-of-bounds memory.
>
> A kernel client processes CEPH_MSG_MDS_MAP from its monitor session
> (net/ceph/mon_client.c dispatches it; fs/ceph/super.c routes it to
> ceph_mdsc_handle_mdsmap(), which sets end to the front buffer bound
> and
> calls ceph_mdsmap_decode()). A malicious or compromised monitor, or
> an
> on-path attacker on an unsigned/unencrypted messenger session, can
> therefore drive an out-of-bounds read in the client kernel; on x86_64
> with KASAN it is reported as a slab-out-of-bounds read in
> ceph_mdsmap_decode(). The decoded values land in the internal
> info->export_targets[] array, so the consequence is a kernel
> out-of-bounds read, not an information leak to the attacker.
>
> Impact: a malicious or compromised Ceph monitor sending an MDS map
> with
> a per-mds info version of 2 or 3 and an oversized num_export_targets
> field triggers an out-of-bounds read in the CephFS client kernel.
>
> Add a ceph_decode_need() for num_export_targets * sizeof(u32) before
> advancing the cursor, so the bound is enforced for every info_v >= 2,
> not only info_v >= 4. This mirrors the count-then-need idiom already
> used for m_data_pg_pools later in the same function.
>
> Fixes: d463a43d69f4 ("ceph: CEPH_FEATURE_MDSENC support")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@xxxxxxxxx>
> ---
>  fs/ceph/mdsmap.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
> index d8e46eb7e5eb5..7cb65f9f4783c 100644
> --- a/fs/ceph/mdsmap.c
> +++ b/fs/ceph/mdsmap.c
> @@ -224,6 +224,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct
> ceph_mds_client *mdsc, void **p,
>   *p += namelen;
>   if (info_v >= 2) {
>   ceph_decode_32_safe(p, end,
> num_export_targets, bad);
> + ceph_decode_need(p, end,
> + num_export_targets *
> sizeof(u32), bad);

We extract the num_export_targets from the message. What if the
num_export_targets is huge enough? I assume we could potentially have
overflow during the multiplication. What do you think?

Thanks,
Slava.

>   pexport_targets = *p;
>   *p += num_export_targets * sizeof(u32);
>   } else {