From f6f89f3b99f57f5c53e698b09e05fa3c9c3cb6ec Mon Sep 17 00:00:00 2001
From: Dominik Loidolt <dominik.loidolt@univie.ac.at>
Date: Tue, 17 Sep 2024 10:50:44 +0200
Subject: [PATCH] Fix: Corrected size calculation when `dst` is NULL in
 `compress_chunk()`

When `dst` is NULL, the returned size was sometimes incorrect by a few
bytes compared to when the `dst` buffer is set. This issue has now been
fixed.
---
 lib/icu_compress/cmp_icu.c          | 10 +++++-----
 test/fuzz/fuzz_compression.c        |  7 +++++++
 test/test_common/chunk_round_trip.c | 15 +++++++++++++++
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/lib/icu_compress/cmp_icu.c b/lib/icu_compress/cmp_icu.c
index c8a7430..45791ee 100644
--- a/lib/icu_compress/cmp_icu.c
+++ b/lib/icu_compress/cmp_icu.c
@@ -1755,23 +1755,23 @@ static uint32_t cmp_collection(const uint8_t *col,
 		cfg->updated_model_buf = updated_model + COLLECTION_HDR_SIZE;
 
 	/* is enough capacity in the dst buffer to store the data uncompressed */
-	if ((!dst || dst_capacity >= dst_size + col_data_length) &&
+	if ((dst == NULL || dst_capacity >= dst_size + col_data_length) &&
 	    cfg->cmp_mode != CMP_MODE_RAW) {
 		/* we set the compressed buffer size to the data size -1 to provoke
 		 * a CMP_ERROR_SMALL_BUF_ error if the data are not compressible
 		 */
 		cfg->stream_size = dst_size + col_data_length - 1;
-		dst_size_bits = compress_data_internal(cfg, dst_size<<3);
+		dst_size_bits = compress_data_internal(cfg, dst_size << 3);
 
 		if (cmp_get_error_code(dst_size_bits) == CMP_ERROR_SMALL_BUF_ ||
-		    (!dst && cmp_bit_to_byte(dst_size_bits)-dst_size > col_data_length)) { /* if dst == NULL compress_data_internal will not return a CMP_ERROR_SMALL_BUF */
+		    (!dst && dst_size_bits > cmp_stream_size_to_bits(cfg->stream_size))) { /* if dst == NULL compress_data_internal will not return a CMP_ERROR_SMALL_BUF */
 			/* can not compress the data with the given parameters;
 			 * put them uncompressed (raw) into the dst buffer */
 			enum cmp_mode cmp_mode_cpy = cfg->cmp_mode;
 
 			cfg->stream_size = dst_size + col_data_length;
 			cfg->cmp_mode = CMP_MODE_RAW;
-			dst_size_bits = compress_data_internal(cfg, dst_size<<3);
+			dst_size_bits = compress_data_internal(cfg, dst_size << 3);
 			cfg->cmp_mode = cmp_mode_cpy;
 			/* updated model is in this case a copy of the data to compress */
 			if (model_mode_is_used(cfg->cmp_mode) && cfg->updated_model_buf)
@@ -1779,7 +1779,7 @@ static uint32_t cmp_collection(const uint8_t *col,
 		}
 	} else {
 		cfg->stream_size = dst_capacity;
-		dst_size_bits = compress_data_internal(cfg, dst_size<<3);
+		dst_size_bits = compress_data_internal(cfg, dst_size << 3);
 	}
 	FORWARD_IF_ERROR(dst_size_bits, "compression failed");
 
diff --git a/test/fuzz/fuzz_compression.c b/test/fuzz/fuzz_compression.c
index 8be34ff..bde54b3 100644
--- a/test/fuzz/fuzz_compression.c
+++ b/test/fuzz/fuzz_compression.c
@@ -133,6 +133,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 	default:
 		FUZZ_ASSERT(0);
 	}
+	if (!cmp_is_error(return_value) && model != up_model){
+		uint32_t return_value2;
+
+		return_value2 = compress_chunk(src, (uint32_t)size, model, up_model,
+					       NULL, cmp_data_capacity, cmp_par_ptr);
+		FUZZ_ASSERT(return_value == return_value2);
+	}
 
 	free(cmp_data);
 	free(up_model);
diff --git a/test/test_common/chunk_round_trip.c b/test/test_common/chunk_round_trip.c
index b51bc48..f23fcea 100644
--- a/test/test_common/chunk_round_trip.c
+++ b/test/test_common/chunk_round_trip.c
@@ -89,6 +89,21 @@ uint32_t chunk_round_trip(const void *chunk, uint32_t chunk_size,
 	cmp_size = compress_chunk(chunk, chunk_size, chunk_model, updated_chunk_model,
 				  dst, dst_capacity, cmp_par);
 
+	if (!cmp_is_error(cmp_size)) { /* secound run wich dst = NULL */
+		uint32_t cmp_size2;
+		/* reset model if in-place update was used */
+		if (chunk_model && updated_chunk_model == chunk_model) {
+			memcpy(updated_chunk_model, model_cpy, chunk_size);
+		}
+		cmp_size2 = compress_chunk(chunk, chunk_size, chunk_model, updated_chunk_model,
+					   NULL, dst_capacity, cmp_par);
+		if (cmp_get_error_code(cmp_size) == CMP_ERROR_SMALL_BUF_) {
+			TEST_ASSERT(!cmp_is_error(cmp_size));
+		} else {
+			TEST_ASSERT(cmp_size == cmp_size2);
+		}
+	}
+
 
 	/* decompress the data if the compression was successful */
 	if (!cmp_is_error(cmp_size) && dst) {
-- 
GitLab