core: Handle arithmetic overflow in adding new fragments

Large fragment sizes can cause arithmetic overflows and can
cause memory corruptions. However, this condition will not be hit
with a fragment size check in place and with a sane memory allocator
in place.

Adding this check to ensure that we have defense in depth.

Signed-off-by: Sumanth Bhat <sumanth.bhat@linux.intel.com>
Change-Id: Iaa976ce636c1eb617afa75e852b39c50699ebeb2
diff --git a/core.c b/core.c
index 84c25d7..2ab5d5f 100644
--- a/core.c
+++ b/core.c
@@ -226,6 +226,10 @@
 
 	len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
 
+	if (len + ctx->buf_size < ctx->buf_size) {
+		return -1;
+	}
+
 	if (ctx->buf_size + len > ctx->buf_alloc_size) {
 		size_t new_alloc_size;
 		void *lbuf;
@@ -234,7 +238,7 @@
 		if (!ctx->buf_alloc_size) {
 			new_alloc_size = MAX(len, 4096UL);
 		} else {
-			new_alloc_size = ctx->buf_alloc_size * 2;
+			new_alloc_size = MAX(ctx->buf_alloc_size * 2, len + ctx->buf_size);
 		}
 
 		/* Don't allow heap to grow beyond a limit */
diff --git a/tests/test_core.c b/tests/test_core.c
index 42d9187..9ea0360 100644
--- a/tests/test_core.c
+++ b/tests/test_core.c
@@ -359,6 +359,35 @@
 	mctp_destroy(mctp);
 }
 
+static void mctp_core_test_drop_large_fragments()
+{
+	struct mctp *mctp = NULL;
+	struct mctp_binding_test *binding = NULL;
+	struct test_params test_param;
+	static uint8_t test_payload[MAX_PAYLOAD_SIZE];
+	struct pktbuf pktbuf;
+
+	memset(test_payload, 0, sizeof(test_payload));
+	test_param.seen = false;
+	test_param.message_size = 0;
+	mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID);
+	mctp_set_rx_all(mctp, rx_message, &test_param);
+	memset(&pktbuf, 0, sizeof(pktbuf));
+	pktbuf.hdr.dest = TEST_DEST_EID;
+	pktbuf.hdr.src = TEST_SRC_EID;
+
+	/* Receive a large payload - first fragment with MCTP_BTU bytes,
+	* 2nd fragment of SIZE_MAX */
+
+	receive_two_fragment_message(binding, test_payload, MCTP_BTU,
+		SIZE_MAX - sizeof(struct mctp_hdr), &pktbuf);
+
+	assert(!test_param.seen);
+
+	mctp_binding_test_destroy(binding);
+	mctp_destroy(mctp);
+}
+
 /* clang-format off */
 #define TEST_CASE(test) { #test, test }
 static const struct {
@@ -371,6 +400,7 @@
 	TEST_CASE(mctp_core_test_receive_unexpected_bigger_middle_fragment),
 	TEST_CASE(mctp_core_test_receive_smaller_end_fragment),
 	TEST_CASE(mctp_core_test_receive_bigger_end_fragment),
+	TEST_CASE(mctp_core_test_drop_large_fragments),
 };
 /* clang-format on */