From 96c437e8a4baf098f47e9b22636ab90fe40c2cf2 Mon Sep 17 00:00:00 2001
From: John Thacker <johnthacker@gmail.com>
Date: Sat, 28 Mar 2026 02:30:04 +0000
Subject: [PATCH] ETSI DCP: Fix heap buffer overflow

The technique used to perform Reed-Solomon decoding in place requires
that extra space be present at the end of the output frame for the
last block's parity bytes and any zeroes (if the code is punctured
because the number of data bytes used is less than the maximum).

Also guard against a few other problems not in the POC; if RSk
is less than the maximum, memset the punctured bytes to 0, and
if RSk in the packet is greater than the maximum, do not attempt
Reed-Solomon decoding (which might have overflows in the calculations.)

Also fix the names of a couple defines which are swapped.

Fix #21122

AI-Assisted: no


(cherry picked from commit e8ef9df09d5a93352e27f2eef5dbb1b65adee0d7)

Co-authored-by: John Thacker <johnthacker@gmail.com>
---
 epan/dissectors/packet-dcp-etsi.c | 36 ++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

Index: wireshark-3.6.24/epan/dissectors/packet-dcp-etsi.c
===================================================================
--- wireshark-3.6.24.orig/epan/dissectors/packet-dcp-etsi.c
+++ wireshark-3.6.24/epan/dissectors/packet-dcp-etsi.c
@@ -177,9 +177,9 @@ dissect_dcp_etsi (tvbuff_t * tvb, packet
   return TRUE;
 }
 
-#define PFT_RS_N_MAX 207
-#define PFT_RS_K 255
-#define PFT_RS_P (PFT_RS_K - PFT_RS_N_MAX)
+#define PFT_RS_K_MAX 207
+#define PFT_RS_N 255
+#define PFT_RS_P (PFT_RS_N - PFT_RS_K_MAX)
 
 
 static
@@ -202,11 +202,23 @@ gboolean rs_correct_data(guint8 *deinter
 {
   guint32 i, index_coded = 0, index_out = 0;
   int err_corr;
+  /* 7.3.1 Reed Solomon
+   * When the calculated value for k is less than 207 (PFT_RS_K_MAX),
+   * bytes k to 206 (inclusive) encoded by the RS(255,207) code shall
+   * all be zero and shall not be included in the resulting RS Block,
+   * thus producing a RS(k+p,k) code. [I.e., a punctured code.]
+   *
+   * This is a method of decoding it all in place, but it does require
+   * that output have extra space at the end for the parity bytes (PFT_RS_P)
+   * and any extra zeros (PFT_RS_K_MAX - rsk) beyond the real output, or
+   * PFT_RS_N - rsk.
+   */
   for (i=0; i<c_max; i++)
   {
     memcpy(output+index_out, deinterleaved+index_coded, rsk);
     index_coded += rsk;
-    memcpy(output+index_out+PFT_RS_N_MAX, deinterleaved+index_coded, PFT_RS_P);
+    memset(output+index_out+rsk, 0, PFT_RS_N - rsk);
+    memcpy(output+index_out+PFT_RS_K_MAX, deinterleaved+index_coded, PFT_RS_P);
     index_coded += PFT_RS_P;
     err_corr = eras_dec_rs(output+index_out, NULL, 0);
     if (err_corr<0) {
@@ -245,6 +257,14 @@ dissect_pft_fec_detailed(tvbuff_t * tvb,
     proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly, tvb , 0, -1, "[Reassembly of %d fragments not attempted]", fcount);
     return NULL;
   }
+  if (fcount == 0) {
+    /* Fcount: ... The value zero shall not be used. */
+    return NULL;
+  }
+  /* RSk == 0 is also nonsensical. */
+  if (rsk > PFT_RS_K_MAX) {
+    return NULL;
+  }
 
   decoded_size = fcount*plen;
   c_max = fcount*plen/(rsk+PFT_RS_P);  /* rounded down */
@@ -316,15 +336,21 @@ dissect_pft_fec_detailed(tvbuff_t * tvb,
     const guint8 *input = tvb_get_ptr(new_tvb, 0, -1);
     guint32 reassembled_size = tvb_captured_length(new_tvb);
     guint8 *deinterleaved = (guint8*) wmem_alloc(pinfo->pool, reassembled_size);
-    guint8 *output = (guint8*) wmem_alloc(pinfo->pool, decoded_size);
     rs_deinterleave(input, deinterleaved, plen, fcount);
 
     dtvb = tvb_new_child_real_data(tvb, deinterleaved, reassembled_size, reassembled_size);
     add_new_data_source(pinfo, dtvb, "Deinterleaved");
 
+    guint8 *output = (guint8*) wmem_alloc(pinfo->pool, decoded_size + PFT_RS_N - rsk);
     decoded = rs_correct_data(deinterleaved, output, c_max, rsk, rsz);
     proto_tree_add_boolean (tree, hf_edcp_rs_ok, tvb, offset, 2, decoded);
 
+#if 0
+    /* We don't need to realloc here because it's pinfo->pool memory that
+     * will soon be freed and < 255 bytes of savings. It's a no-op most
+     * likely with the fast block allocator anyway. */
+    output = wmem_realloc(pinfo->pool, output, decoded_size);
+#endif
     new_tvb = tvb_new_child_real_data(dtvb, output, decoded_size, decoded_size);
     add_new_data_source(pinfo, new_tvb, "RS Error Corrected Data");
   }
