[PATCH 5.15 152/570] libceph: reject preamble if control segment is empty

Greg Kroah-Hartman
2026-04-13T16:24:41Z

From: Ilya Dryomov <idryomov@gmail.com>

commit c4c22b846eceff05b1129b8844a80310e55a7f87 upstream.

While head_onwire_len() has a branch to handle ctrl_len == 0 case,
prepare_read_control() always sets up a kvec for the CRC meaning that
a non-empty control segment is effectively assumed. All frames that
clients deal with meet that assumption, so let's make it official and
treat the preamble with an empty control segment as malformed.

Cc: stable@vger.kernel.org
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Alex Markuze <amarkuze@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
net/ceph/messenger_v2.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)

--- a/net/ceph/messenger_v2.c
+++ b/net/ceph/messenger_v2.c
@@ -391,7 +391,7 @@ static int head_onwire_len(int ctrl_len,
int head_len;
int rem_len;

- BUG_ON(ctrl_len < 0 || ctrl_len > CEPH_MSG_MAX_CONTROL_LEN);
+ BUG_ON(ctrl_len < 1 || ctrl_len > CEPH_MSG_MAX_CONTROL_LEN);

if (secure) {
head_len = CEPH_PREAMBLE_SECURE_LEN;
@@ -400,9 +400,7 @@ static int head_onwire_len(int ctrl_len,
head_len += padded_len(rem_len) + CEPH_GCM_TAG_LEN;
}
} else {
- head_len = CEPH_PREAMBLE_PLAIN_LEN;
- if (ctrl_len)
- head_len += ctrl_len + CEPH_CRC_LEN;
+ head_len = CEPH_PREAMBLE_PLAIN_LEN + ctrl_len + CEPH_CRC_LEN;
}
return head_len;
}
@@ -527,11 +525,16 @@ static int decode_preamble(void *p, stru
desc->fd_aligns[i] = ceph_decode_16(&p);
}

- if (desc->fd_lens[0] < 0 ||
+ /*
+ * This would fire for FRAME_TAG_WAIT (it has one empty
+ * segment), but we should never get it as client.
+ */
+ if (desc->fd_lens[0] < 1 ||
desc->fd_lens[0] > CEPH_MSG_MAX_CONTROL_LEN) {
pr_err("bad control segment length %d\n", desc->fd_lens[0]);
return -EINVAL;
}
+
if (desc->fd_lens[1] < 0 ||
desc->fd_lens[1] > CEPH_MSG_MAX_FRONT_LEN) {
pr_err("bad front segment length %d\n", desc->fd_lens[1]);
@@ -548,10 +551,6 @@ static int decode_preamble(void *p, stru
return -EINVAL;
}

- /*
- * This would fire for FRAME_TAG_WAIT (it has one empty
- * segment), but we should never get it as client.
- */
if (!desc->fd_lens[desc->fd_seg_cnt - 1]) {
pr_err("last segment empty, segment count %d\n",
desc->fd_seg_cnt);