From 0e50b8600d491fd4001c20aecccf3abe1ea199ac Mon Sep 17 00:00:00 2001
From: Martin Schwenke <mschwenke@ddn.com>
Date: Wed, 10 Jun 2026 12:37:02 +1000
Subject: [PATCH 14/23] CVE-2026-58224: ctdb-protocol: Avoid DoS memory allocations

The pull loop already avoids out of bounds accesses beyond the end of
the buffer.  However, it does not avoid a DoS memory allocation due to
an unreasonably large array size.

Check that the number of specified array elements can be pulled from
buffer, which puts a reasonable upper bound on the subsequent memory
allocation.

Use an initialised dummy variable to avoid static analysers
complaining about uninitialised variables being passed.

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

Signed-off-by: Martin Schwenke <mschwenke@ddn.com>
Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
---
 ctdb/protocol/protocol_types.c | 35 ++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/ctdb/protocol/protocol_types.c b/ctdb/protocol/protocol_types.c
index 2edf8228d18..8cbdaee5106 100644
--- a/ctdb/protocol/protocol_types.c
+++ b/ctdb/protocol/protocol_types.c
@@ -2380,6 +2380,7 @@ int ctdb_connection_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 			      struct ctdb_connection_list **out, size_t *npull)
 {
 	struct ctdb_connection_list *val;
+	struct ctdb_connection dummy = {};
 	size_t offset = 0, np;
 	uint32_t i;
 	int ret;
@@ -2400,6 +2401,11 @@ int ctdb_connection_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
 	val->conn = talloc_array(val, struct ctdb_connection, val->num);
 	if (val->conn == NULL) {
 		ret = ENOMEM;
@@ -3417,6 +3423,7 @@ int ctdb_tickle_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 			   struct ctdb_tickle_list **out, size_t *npull)
 {
 	struct ctdb_tickle_list *val;
+	struct ctdb_connection dummy = {};
 	size_t offset = 0, np;
 	uint32_t i;
 	int ret;
@@ -3444,6 +3451,11 @@ int ctdb_tickle_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
 	val->conn = talloc_array(val, struct ctdb_connection, val->num);
 	if (val->conn == NULL) {
 		ret = ENOMEM;
@@ -3762,6 +3774,7 @@ int ctdb_public_ip_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 			     struct ctdb_public_ip_list **out, size_t *npull)
 {
 	struct ctdb_public_ip_list *val;
+	struct ctdb_public_ip dummy = {};
 	size_t offset = 0, np;
 	uint32_t i;
 	int ret;
@@ -3782,6 +3795,11 @@ int ctdb_public_ip_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	if ((uint64_t)val->num * ctdb_public_ip_len(&dummy) > buflen - offset) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
 	val->ip = talloc_array(val, struct ctdb_public_ip, val->num);
 	if (val->ip == NULL) {
 		ret = ENOMEM;
@@ -4124,6 +4142,7 @@ int ctdb_script_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 			  struct ctdb_script_list **out, size_t *npull)
 {
 	struct ctdb_script_list *val;
+	struct ctdb_script dummy = {};
 	size_t offset = 0, np;
 	uint32_t i;
 	int ret;
@@ -4157,6 +4176,12 @@ int ctdb_script_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	if ((uint64_t)val->num_scripts * ctdb_script_len(&dummy)
+	    > buflen - offset) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
 	val->script = talloc_array(val, struct ctdb_script, val->num_scripts);
 	if (val->script == NULL) {
 		ret = ENOMEM;
@@ -4400,6 +4425,7 @@ int ctdb_iface_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 			 struct ctdb_iface_list **out, size_t *npull)
 {
 	struct ctdb_iface_list *val;
+	struct ctdb_iface dummy = {};
 	size_t offset = 0, np;
 	uint32_t i;
 	int ret;
@@ -4420,6 +4446,11 @@ int ctdb_iface_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 		goto done;
 	}
 
+	if ((uint64_t)val->num * ctdb_iface_len(&dummy) > buflen - offset) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
 	val->iface = talloc_array(val, struct ctdb_iface, val->num);
 	if (val->iface == NULL) {
 		ret = ENOMEM;
@@ -5347,6 +5378,10 @@ int ctdb_g_lock_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 
 	val->num = buflen / ctdb_g_lock_len(&lock);
 
+	/*
+	 * No array count pre-check needed because val->num is
+	 * calculated from buflen
+	 */
 	val->lock = talloc_array(val, struct ctdb_g_lock, val->num);
 	if (val->lock == NULL) {
 		ret = ENOMEM;
-- 
2.43.0


