From f252a17bc8ae7568943b0f4cf526d1f8555d6429 Mon Sep 17 00:00:00 2001
From: Martin Schwenke <mschwenke@ddn.com>
Date: Wed, 27 May 2026 18:59:24 +1000
Subject: [PATCH 03/23] CVE-2026-58224: ctdb-daemon: Avoid out of bounds data access

Protocol field lengths need to be validated to avoid attempts to
access memory beyond the end of the packet buffer.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16085

Reported-by: Tristan Madani <tristan@talencesecurity.com>
Signed-off-by: Martin Schwenke <mschwenke@ddn.com>
Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
---
 ctdb/server/ctdb_call.c    | 38 ++++++++++++++++++++++++++++++++++++++
 ctdb/server/ctdb_client.c  |  9 +++++++++
 ctdb/server/ctdb_control.c | 19 +++++++++++++++++++
 3 files changed, 66 insertions(+)

--- a/ctdb/server/ctdb_call.c
+++ b/ctdb/server/ctdb_call.c
@@ -583,6 +583,9 @@ static int dmaster_defer_add(struct ctdb
 void ctdb_request_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_req_dmaster_old *c = (struct ctdb_req_dmaster_old *)hdr;
+	size_t req_dmaster_header_len = offsetof(struct ctdb_req_dmaster_old,
+						 data);
+	size_t buf_len = hdr->length;
 	TDB_DATA key, data, data2;
 	struct ctdb_ltdb_header header;
 	struct ctdb_db_context *ctdb_db;
@@ -590,6 +593,13 @@ void ctdb_request_dmaster(struct ctdb_co
 	size_t len;
 	int ret;
 
+	if (buf_len < req_dmaster_header_len ||
+	    c->keylen > buf_len - req_dmaster_header_len ||
+	    c->datalen > buf_len - req_dmaster_header_len - c->keylen) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	key.dptr = c->data;
 	key.dsize = c->keylen;
 	data.dptr = c->data + c->keylen;
@@ -917,6 +927,8 @@ sort_keys:
 void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_req_call_old *c = (struct ctdb_req_call_old *)hdr;
+	size_t req_call_header_len = offsetof(struct ctdb_req_call_old, data);
+	size_t buf_len = hdr->length;
 	TDB_DATA data;
 	struct ctdb_reply_call_old *r;
 	int ret, len;
@@ -925,6 +937,13 @@ void ctdb_request_call(struct ctdb_conte
 	struct ctdb_db_context *ctdb_db;
 	int tmp_count, bucket;
 
+	if (buf_len < req_call_header_len ||
+	    c->keylen > buf_len - req_call_header_len  ||
+	    c->calldatalen > buf_len - req_call_header_len - c->keylen) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	if (ctdb->methods == NULL) {
 		DEBUG(DEBUG_INFO,(__location__ " Failed ctdb_request_call. Transport is DOWN\n"));
 		return;
@@ -1199,8 +1218,17 @@ void ctdb_request_call(struct ctdb_conte
 void ctdb_reply_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_reply_call_old *c = (struct ctdb_reply_call_old *)hdr;
+	size_t reply_call_header_len = offsetof(struct ctdb_reply_call_old,
+						data);
+	size_t buf_len = hdr->length;
 	struct ctdb_call_state *state;
 
+	if (buf_len < reply_call_header_len ||
+	    c->datalen > buf_len - reply_call_header_len) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_call_state);
 	if (state == NULL) {
 		DEBUG(DEBUG_ERR, (__location__ " reqid %u not found\n", hdr->reqid));
@@ -1296,12 +1324,22 @@ finished_ro:
 void ctdb_reply_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_reply_dmaster_old *c = (struct ctdb_reply_dmaster_old *)hdr;
+	size_t reply_dmaster_header_len = offsetof(struct ctdb_reply_dmaster_old,
+						   data);
+	size_t buf_len = hdr->length;
 	struct ctdb_db_context *ctdb_db;
 	TDB_DATA key, data;
 	uint32_t record_flags = 0;
 	size_t len;
 	int ret;
 
+	if (buf_len < reply_dmaster_header_len ||
+	    c->keylen > buf_len - reply_dmaster_header_len ||
+	    c->datalen > buf_len - reply_dmaster_header_len - c->keylen) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	ctdb_db = find_ctdb_db(ctdb, c->db_id);
 	if (ctdb_db == NULL) {
 		DEBUG(DEBUG_ERR,("Unknown db_id 0x%x in ctdb_reply_dmaster\n", c->db_id));
--- a/ctdb/server/ctdb_client.c
+++ b/ctdb/server/ctdb_client.c
@@ -193,8 +193,17 @@ void ctdb_request_message(struct ctdb_co
 			  struct ctdb_req_header *hdr)
 {
 	struct ctdb_req_message_old *c = (struct ctdb_req_message_old *)hdr;
+	size_t req_message_header_len = offsetof(struct ctdb_req_message_old,
+						 data);
+	size_t buf_len = hdr->length;
 	TDB_DATA data;
 
+	if (buf_len < req_message_header_len ||
+	    c->datalen > buf_len - req_message_header_len) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	data.dsize = c->datalen;
 	data.dptr = talloc_memdup(c, &c->data[0], c->datalen);
 	if (data.dptr == NULL) {
--- a/ctdb/server/ctdb_control.c
+++ b/ctdb/server/ctdb_control.c
@@ -922,11 +922,20 @@ void ctdb_request_control_reply(struct c
 void ctdb_request_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_req_control_old *c = (struct ctdb_req_control_old *)hdr;
+	size_t req_control_header_len = offsetof(struct ctdb_req_control_old,
+						 data);
+	size_t buf_len = hdr->length;
 	TDB_DATA data, *outdata;
 	int32_t status;
 	bool async_reply = false;
 	const char *errormsg = NULL;
 
+	if (buf_len < req_control_header_len ||
+	    c->datalen > buf_len - req_control_header_len) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	data.dptr = &c->data[0];
 	data.dsize = c->datalen;
 
@@ -946,10 +955,20 @@ void ctdb_request_control(struct ctdb_co
 void ctdb_reply_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
 	struct ctdb_reply_control_old *c = (struct ctdb_reply_control_old *)hdr;
+	size_t reply_control_header_len = offsetof(struct ctdb_reply_control_old,
+						   data);
+	size_t buf_len = hdr->length;
 	TDB_DATA data;
 	struct ctdb_control_state *state;
 	const char *errormsg = NULL;
 
+	if (buf_len < reply_control_header_len ||
+	    c->datalen > buf_len - reply_control_header_len ||
+	    c->errorlen > buf_len - reply_control_header_len - c->datalen) {
+		DBG_WARNING("Invalid packet\n");
+		return;
+	}
+
 	state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_control_state);
 	if (state == NULL) {
 		DEBUG(DEBUG_ERR,("pnn %u Invalid reqid %u in ctdb_reply_control\n",
