From: Viacheslav Dubeyko Date: Tue, 16 Sep 2025 18:58:28 +0000 (-0700) Subject: ceph: fix potential overflow in parse_reply_info_dir() X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=709187a80fbdb8545c8dc1c952904cfbf46a0933;p=ceph-client.git ceph: fix potential overflow in parse_reply_info_dir() 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 Signed-off-by: Al Viro Signed-off-by: Viacheslav Dubeyko cc: Alex Markuze cc: Ilya Dryomov cc: Ceph Development --- diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6a9f5b4f5088..61be770c7486 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -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;