]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph: fix potential overflow in parse_reply_info_dir()
authorViacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Tue, 16 Sep 2025 18:58:28 +0000 (11:58 -0700)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 11 Feb 2026 18:19:17 +0000 (19:19 +0100)
The parse_reply_info_dir() logic tries to parse
a dir fragment:

struct ceph_mds_reply_dirfrag {
__le32 frag;            /* fragment */
__le32 auth;            /* auth mds, if this is a delegation point */
__le32 ndist;           /* number of mds' this is replicated on */
__le32 dist[];
} __attribute__ ((packed));

Potentially, ndist field could be corrupted or to have
invalid or malicious value. As a result, this logic
could result in overflow:

*p += sizeof(**dirfrag) + sizeof(u32) * le32_to_cpu((*dirfrag)->ndist);

Al Viro suggested the initial vision of the fix.
The suggested fix was partially reworked.

This patch adds the checking that ndist is not bigger
than (U32_MAX / sizeof(u32)) and to check that we have
enough space in memory buffer by means of ceph_decode_need().

Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
cc: Alex Markuze <amarkuze@redhat.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Ceph Development <ceph-devel@vger.kernel.org>

fs/ceph/mds_client.c

index 6a9f5b4f508807f4482f3cf921812fc334c1f38c..61be770c7486d33288fbc5f0dcc53e6f3d404a5d 100644 (file)
@@ -284,6 +284,11 @@ static int parse_reply_info_dir(void **p, void *end,
                                struct ceph_mds_reply_dirfrag **dirfrag,
                                u64 features)
 {
+       size_t item_size = sizeof(u32);
+       size_t dirfrag_size;
+       u32 ndist;
+       u32 array_size;
+
        if (features == (u64)-1) {
                u8 struct_v, struct_compat;
                u32 struct_len;
@@ -298,11 +303,16 @@ static int parse_reply_info_dir(void **p, void *end,
                end = *p + struct_len;
        }
 
-       ceph_decode_need(p, end, sizeof(**dirfrag), bad);
+       dirfrag_size = sizeof(**dirfrag);
+       ceph_decode_need(p, end, dirfrag_size, bad);
        *dirfrag = *p;
-       *p += sizeof(**dirfrag) + sizeof(u32) * le32_to_cpu((*dirfrag)->ndist);
-       if (unlikely(*p > end))
+       *p += dirfrag_size;
+       ndist = le32_to_cpu((*dirfrag)->ndist);
+       if (unlikely(ndist > (U32_MAX / item_size)))
                goto bad;
+       array_size = ndist * item_size;
+       ceph_decode_need(p, end, array_size, bad);
+       *p += array_size;
        if (features == (u64)-1)
                *p = end;
        return 0;