From c4fb4bd6d49dcb1e118d510fc6ac9df37366edb6 Mon Sep 17 00:00:00 2001
From: John Thacker <johnthacker@gmail.com>
Date: Tue, 14 Apr 2026 11:00:56 -0400
Subject: [PATCH] SMB2: Put bounds on total chained compression

Stop when the total output of the chained compression is more
than our maximum.

Once we get an error (or give up because it's too long), keep
adding fields to the tree but stop wasting cycles decompressing
when the results will just get thrown away.

An unknown compression type is an error.

Only add to the Info column for one error for the same chained
compression.

Fix #21191

AI-Assisted: no


(cherry picked from commit 8e3303b3d2d1697c6255a6f92bcab4e7abcf367d)

Co-authored-by: John Thacker <johnthacker@gmail.com>
---
 epan/dissectors/packet-smb2.c | 37 ++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 14 deletions(-)

Index: wireshark-3.6.24/epan/dissectors/packet-smb2.c
===================================================================
--- wireshark-3.6.24.orig/epan/dissectors/packet-smb2.c
+++ wireshark-3.6.24/epan/dissectors/packet-smb2.c
@@ -10284,7 +10284,7 @@ append_uncompress_data(wmem_array_t *out
 static int
 dissect_smb2_compression_pattern_v1(proto_tree *tree,
 				    tvbuff_t *tvb, int offset, int length,
-				    wmem_array_t *out)
+				    wmem_array_t *out, bool *ok)
 {
 	proto_item *pat_item;
 	proto_tree *pat_tree;
@@ -10308,7 +10308,11 @@ dissect_smb2_compression_pattern_v1(prot
 
 	proto_item_append_text(pat_item, " 0x%02x repeated %u times", pattern, times);
 
-	if (out && times < MAX_UNCOMPRESSED_SIZE) {
+	if (times > MAX_UNCOMPRESSED_SIZE) {
+		*ok = false;
+	}
+
+	if (out && *ok) {
 		guint8 v = (guint8)pattern;
 
 		for (guint i = 0; i < times; i++)
@@ -10330,8 +10334,6 @@ dissect_smb2_chained_comp_payload(packet
 	tvbuff_t *uncomp_tvb = NULL;
 	gboolean lz_based = FALSE;
 
-	*ok = TRUE;
-
 	subtree = proto_tree_add_subtree_format(tree, tvb, offset, 0, ett_smb2_comp_payload, &subitem, "COMPRESSION_PAYLOAD_HEADER");
 	proto_tree_add_item_ret_uint(subtree, hf_smb2_comp_transform_comp_alg, tvb, offset, 2, ENC_LITTLE_ENDIAN, &alg);
 	offset += 2;
@@ -10352,10 +10354,15 @@ dissect_smb2_chained_comp_payload(packet
 		length -= 4;
 	}
 
-	if (length > MAX_UNCOMPRESSED_SIZE) {
+	if (length > MAX_UNCOMPRESSED_SIZE || wmem_array_get_count(out) > MAX_UNCOMPRESSED_SIZE) {
 		/* decompression error */
-		col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (invalid)");
-		*ok = FALSE;
+		if (*ok) {
+			col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (too big)");
+		}
+ 		*ok = false;
+	}
+	if (!*ok && (lz_based || SMB2_COMP_ALG_NONE)) {
+
 		goto out;
 	}
 
@@ -10373,19 +10380,24 @@ dissect_smb2_chained_comp_payload(packet
 		uncomp_tvb = tvb_uncompress_lznt1(tvb, offset, length);
 		break;
 	case SMB2_COMP_ALG_PATTERN_V1:
-		dissect_smb2_compression_pattern_v1(subtree, tvb, offset, length, out);
+		dissect_smb2_compression_pattern_v1(subtree, tvb, offset, length, out, ok);
 		break;
 	default:
-		col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (unknown)");
+		// XXX - This should be an expert info, probably not in column
+		if (*ok) {
+			col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (unknown)");
+		}
 		uncomp_tvb = NULL;
+		*ok = false;
 		break;
 	}
 
 	if (lz_based) {
 		if (!uncomp_tvb || tvb_reported_length(uncomp_tvb) != orig_size) {
 			/* decompression error */
-			col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (invalid)");
-			*ok = FALSE;
+			if (*ok) {
+				col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (invalid)");
+			}			*ok = FALSE;
 			goto out;
 		}
 		append_uncompress_data(out, uncomp_tvb, 0, tvb_reported_length(uncomp_tvb));
@@ -10448,9 +10460,8 @@ dissect_smb2_comp_transform_header(packe
 		do {
 			gboolean ok = FALSE;
 
-			offset = dissect_smb2_chained_comp_payload(pinfo, tree, tvb, offset, uncomp_data, &ok);
-			if (!ok)
-				all_ok = FALSE;
+			offset = dissect_smb2_chained_comp_payload(pinfo, tree, tvb, offset, uncomp_data, &all_ok);
+
 		} while (tvb_reported_length_remaining(tvb, offset) > 8);
 		if (all_ok)
 			goto decompression_ok;
