Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:protobuf
User: [email protected]
Usertags: pu

  * Fix CVE-2026-0994: JSON recursion depth bypass (closes: #1126302).
  * Fix CVE-2026-6409: PHP Denial of Service (closes: #1134895).
  * Complete fix of CVE-2024-7254 (closes: #1082381):
    - add recursion checks and recursion limit,
    - add tests.
  * Fix CVE-2025-4565: data containing an arbitrary number of recursive
    groups, recursive messages or a series of SGROUP tags can be corrupted
    by exceeding the Python recursion limit (closes: #1108057).
diffstat for protobuf-3.21.12 protobuf-3.21.12

 changelog                                                               |   18 
 patches/0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch |  169 
++
 patches/0002-Check-that-readRaw-does-not-accept-negative-length-v.patch |   48 
 patches/0003-php-Fix-that-recursion-limit-is-not-enforced.patch         |   65 
 patches/CVE-2024-7254-1.patch                                           |  202 
++
 patches/CVE-2024-7254-2.patch                                           |  143 
++
 patches/CVE-2024-7254-3.patch                                           |  684 
++++++++++
 patches/CVE-2024-7254-4.patch                                           |   21 
 patches/CVE-2024-7254-5.patch                                           |   66 
 patches/CVE-2024-7254.patch                                             |  143 
--
 patches/CVE-2025-4565-1.patch                                           |  117 
+
 patches/CVE-2025-4565-2.patch                                           |  395 
+++++
 patches/CVE-2025-4565-3.patch                                           |  207 
+++
 patches/series                                                          |   12 
 14 files changed, 2146 insertions(+), 144 deletions(-)

diff -Nru protobuf-3.21.12/debian/changelog protobuf-3.21.12/debian/changelog
--- protobuf-3.21.12/debian/changelog   2025-04-29 22:27:02.000000000 +0300
+++ protobuf-3.21.12/debian/changelog   2026-07-02 22:28:07.000000000 +0300
@@ -1,3 +1,21 @@
+protobuf (3.21.12-11+deb13u1) trixie; urgency=medium
+
+  * Non-maintainer upload.
+  * Fix CVE-2026-0994: JSON recursion depth bypass (closes: #1126302).
+  * Fix CVE-2026-6409: PHP Denial of Service (closes: #1134895).
+
+  [ Hlib Korzhynskyy <[email protected]> ]
+  * Complete fix of CVE-2024-7254 (closes: #1082381):
+    - add recursion checks and recursion limit,
+    - add tests.
+
+  [ Laszlo Boszormenyi (GCS) ]
+  * Fix CVE-2025-4565: data containing an arbitrary number of recursive
+    groups, recursive messages or a series of SGROUP tags can be corrupted
+    by exceeding the Python recursion limit (closes: #1108057).
+
+ -- Adrian Bunk <[email protected]>  Thu, 02 Jul 2026 22:28:07 +0300
+
 protobuf (3.21.12-11) unstable; urgency=high
 
   * Fix CVE-2024-7254: when parsing unknown fields in the Protobuf Java Lite
diff -Nru 
protobuf-3.21.12/debian/patches/0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch
 
protobuf-3.21.12/debian/patches/0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch
--- 
protobuf-3.21.12/debian/patches/0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch
     1970-01-01 02:00:00.000000000 +0200
+++ 
protobuf-3.21.12/debian/patches/0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch
     2026-07-02 22:07:59.000000000 +0300
@@ -0,0 +1,169 @@
+From 425bbbbb1bba4f76cbae984b75fefbc84666d801 Mon Sep 17 00:00:00 2001
+From: zhangskz <[email protected]>
+Date: Thu, 29 Jan 2026 16:32:56 -0500
+Subject: Fix Any recursion depth bypass in Python json_format.ParseDict
+ (#25239) (#25587)
+
+This fixes a security vulnerability where nested google.protobuf.Any messages 
could bypass the max_recursion_depth limit, potentially leading to denial of 
service via stack overflow.
+
+The root cause was that _ConvertAnyMessage() was calling itself recursively 
via methodcaller() for nested well-known types, bypassing the recursion depth 
tracking in ConvertMessage().
+
+The fix routes well-known type parsing through ConvertMessage() to ensure 
proper recursion depth accounting for all message types including nested Any.
+
+Fixes #25070
+
+Closes #25239
+
+COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/25239
 from aviralgarg05:fix-any-recursion-depth-bypass 
3cbbcbea142593d3afd2ceba2db14b05660f62f4
+PiperOrigin-RevId: 862740421
+
+Co-authored-by: Aviral Garg <[email protected]>
+---
+ .../protobuf/internal/json_format_test.py     | 101 ++++++++++++++++++
+ python/google/protobuf/json_format.py         |  12 ++-
+ 2 files changed, 110 insertions(+), 3 deletions(-)
+
+diff --git a/python/google/protobuf/internal/json_format_test.py 
b/python/google/protobuf/internal/json_format_test.py
+index 7b7d3bba4..8eb9dfd0a 100644
+--- a/python/google/protobuf/internal/json_format_test.py
++++ b/python/google/protobuf/internal/json_format_test.py
+@@ -1281,5 +1281,106 @@ class JsonFormatTest(JsonFormatBase):
+     json_format.Parse('{"payload": {}, "child": {"child":{}}}',
+                       message, max_recursion_depth=3)
+ 
++  def testAnyRecursionDepthEnforcement(self):
++    """Test that nested Any messages respect max_recursion_depth limit."""
++    # Test that deeply nested Any messages raise ParseError instead of
++    # bypassing the recursion limit. This prevents DoS via nested Any.
++    message = any_pb2.Any()
++
++    # Create nested Any structure that should exceed depth limit
++    # With max_recursion_depth=5, we can nest 4 Any messages
++    # (depth 1 = outer Any, depth 2-4 = nested Anys, depth 5 = final value)
++    nested_any = {
++        '@type': 'type.googleapis.com/google.protobuf.Any',
++        'value': {
++            '@type': 'type.googleapis.com/google.protobuf.Any',
++            'value': {
++                '@type': 'type.googleapis.com/google.protobuf.Any',
++                'value': {
++                    '@type': 'type.googleapis.com/google.protobuf.Any',
++                    'value': {
++                        '@type': 'type.googleapis.com/google.protobuf.Any',
++                        'value': {},
++                    },
++                },
++            },
++        },
++    }
++
++    # Should raise ParseError due to exceeding max depth, not RecursionError
++    self.assertRaisesRegex(
++        json_format.ParseError,
++        'Message too deep. Max recursion depth is 5',
++        json_format.ParseDict,
++        nested_any,
++        message,
++        max_recursion_depth=5,
++    )
++
++    # Verify that Any messages within the limit can be parsed successfully
++    # With max_recursion_depth=5, we can nest up to 4 Any messages
++    shallow_any = {
++        '@type': 'type.googleapis.com/google.protobuf.Any',
++        'value': {
++            '@type': 'type.googleapis.com/google.protobuf.Any',
++            'value': {
++                '@type': 'type.googleapis.com/google.protobuf.Any',
++                'value': {
++                    '@type': 'type.googleapis.com/google.protobuf.Any',
++                    'value': {},
++                },
++            },
++        },
++    }
++    json_format.ParseDict(shallow_any, message, max_recursion_depth=5)
++
++  def testAnyRecursionDepthBoundary(self):
++    """Test recursion depth boundary behavior (exclusive upper limit)."""
++    message = any_pb2.Any()
++
++    # Create nested Any at depth exactly 4 (should succeed with 
max_recursion_depth=5)
++    depth_4_any = {
++        '@type': 'type.googleapis.com/google.protobuf.Any',
++        'value': {
++            '@type': 'type.googleapis.com/google.protobuf.Any',
++            'value': {
++                '@type': 'type.googleapis.com/google.protobuf.Any',
++                'value': {
++                    '@type': 'type.googleapis.com/google.protobuf.Any',
++                    'value': {},
++                },
++            },
++        },
++    }
++    # This should succeed: depth 4 < max_recursion_depth 5
++    json_format.ParseDict(depth_4_any, message, max_recursion_depth=5)
++
++    # Create nested Any at depth exactly 5 (should fail with 
max_recursion_depth=5)
++    depth_5_any = {
++        '@type': 'type.googleapis.com/google.protobuf.Any',
++        'value': {
++            '@type': 'type.googleapis.com/google.protobuf.Any',
++            'value': {
++                '@type': 'type.googleapis.com/google.protobuf.Any',
++                'value': {
++                    '@type': 'type.googleapis.com/google.protobuf.Any',
++                    'value': {
++                        '@type': 'type.googleapis.com/google.protobuf.Any',
++                        'value': {},
++                    },
++                },
++            },
++        },
++    }
++    # This should fail: depth 5 == max_recursion_depth 5 (exclusive limit)
++    self.assertRaisesRegex(
++        json_format.ParseError,
++        'Message too deep. Max recursion depth is 5',
++        json_format.ParseDict,
++        depth_5_any,
++        message,
++        max_recursion_depth=5,
++    )
++
+ if __name__ == '__main__':
+   unittest.main()
+diff --git a/python/google/protobuf/json_format.py 
b/python/google/protobuf/json_format.py
+index 5024ed89d..ff7e3c6e5 100644
+--- a/python/google/protobuf/json_format.py
++++ b/python/google/protobuf/json_format.py
+@@ -486,6 +486,10 @@ class _Parser(object):
+     Raises:
+       ParseError: In case of convert problems.
+     """
++    # Increment recursion depth at message entry. The max_recursion_depth 
limit
++    # is exclusive: a depth value equal to max_recursion_depth will trigger an
++    # error. For example, with max_recursion_depth=5, nesting up to depth 4 is
++    # allowed, but attempting depth 5 raises ParseError.
+     self.recursion_depth += 1
+     if self.recursion_depth > self.max_recursion_depth:
+       raise ParseError('Message too deep. Max recursion depth is {0}'.format(
+@@ -652,9 +656,11 @@ class _Parser(object):
+       self._ConvertWrapperMessage(value['value'], sub_message,
+                                   '{0}.value'.format(path))
+     elif full_name in _WKTJSONMETHODS:
+-      methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message,
+-                   '{0}.value'.format(path))(
+-                       self)
++      # For well-known types (including nested Any), use ConvertMessage
++      # to ensure recursion depth is properly tracked
++      self.ConvertMessage(
++          value['value'], sub_message, '{0}.value'.format(path)
++      )
+     else:
+       del value['@type']
+       self._ConvertFieldValuePair(value, sub_message, path)
+-- 
+2.47.3
+
diff -Nru 
protobuf-3.21.12/debian/patches/0002-Check-that-readRaw-does-not-accept-negative-length-v.patch
 
protobuf-3.21.12/debian/patches/0002-Check-that-readRaw-does-not-accept-negative-length-v.patch
--- 
protobuf-3.21.12/debian/patches/0002-Check-that-readRaw-does-not-accept-negative-length-v.patch
     1970-01-01 02:00:00.000000000 +0200
+++ 
protobuf-3.21.12/debian/patches/0002-Check-that-readRaw-does-not-accept-negative-length-v.patch
     2026-07-02 22:07:59.000000000 +0300
@@ -0,0 +1,48 @@
+From 8d3c56f84a1158095f28ef859757872df6c176cc Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Tue, 13 Jan 2026 12:31:18 -0800
+Subject: Check that `readRaw` does not accept negative length value.
+
+Fixes https://github.com/protocolbuffers/protobuf/issues/24159
+
+PiperOrigin-RevId: 855837030
+---
+ php/src/Google/Protobuf/Internal/CodedInputStream.php | 3 ++-
+ php/tests/EncodeDecodeTest.php                        | 7 +++++++
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/php/src/Google/Protobuf/Internal/CodedInputStream.php 
b/php/src/Google/Protobuf/Internal/CodedInputStream.php
+index 2ed2dfde2..0f98be7a6 100644
+--- a/php/src/Google/Protobuf/Internal/CodedInputStream.php
++++ b/php/src/Google/Protobuf/Internal/CodedInputStream.php
+@@ -295,7 +295,8 @@ class CodedInputStream
+     public function readRaw($size, &$buffer)
+     {
+         $current_buffer_size = 0;
+-        if ($this->bufferSize() < $size) {
++        // size (varint) read from the wire could be negative.
++        if ($size < 0 || $this->bufferSize() < $size) {
+             return false;
+         }
+ 
+diff --git a/php/tests/EncodeDecodeTest.php b/php/tests/EncodeDecodeTest.php
+index 33d8da179..ddd01a1ac 100644
+--- a/php/tests/EncodeDecodeTest.php
++++ b/php/tests/EncodeDecodeTest.php
+@@ -538,6 +538,13 @@ class EncodeDecodeTest extends TestBase
+         $this->assertEquals(-1, $m->getOptionalInt32());
+     }
+ 
++    public function testInvalidVarintLength() {
++        $this->expectException(Exception::class);
++
++        $m = new TestMessage();
++        $m->mergeFromString(hex2bin("0afaffffff0f"));
++    }
++
+     public function testRandomFieldOrder()
+     {
+         $m = new TestRandomFieldOrder();
+-- 
+2.47.3
+
diff -Nru 
protobuf-3.21.12/debian/patches/0003-php-Fix-that-recursion-limit-is-not-enforced.patch
 
protobuf-3.21.12/debian/patches/0003-php-Fix-that-recursion-limit-is-not-enforced.patch
--- 
protobuf-3.21.12/debian/patches/0003-php-Fix-that-recursion-limit-is-not-enforced.patch
     1970-01-01 02:00:00.000000000 +0200
+++ 
protobuf-3.21.12/debian/patches/0003-php-Fix-that-recursion-limit-is-not-enforced.patch
     2026-07-02 22:07:59.000000000 +0300
@@ -0,0 +1,65 @@
+From 16db0faafd1c7357a7d91e53dc7deea10e9bbab9 Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Wed, 14 Jan 2026 11:09:03 -0800
+Subject: php: Fix that recursion limit is not enforced.
+
+https://github.com/protocolbuffers/protobuf/issues/25067
+
+PiperOrigin-RevId: 856286406
+---
+ .../Protobuf/Internal/CodedInputStream.php    |  2 +-
+ php/tests/EncodeDecodeTest.php                | 25 +++++++++++++++++++
+ 2 files changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/php/src/Google/Protobuf/Internal/CodedInputStream.php 
b/php/src/Google/Protobuf/Internal/CodedInputStream.php
+index 0f98be7a6..52e8f5473 100644
+--- a/php/src/Google/Protobuf/Internal/CodedInputStream.php
++++ b/php/src/Google/Protobuf/Internal/CodedInputStream.php
+@@ -362,7 +362,7 @@ class CodedInputStream
+         $byte_limit, &$old_limit, &$recursion_budget)
+     {
+         $old_limit = $this->pushLimit($byte_limit);
+-        $recursion_limit = --$this->recursion_limit;
++        $recursion_budget = --$this->recursion_budget;
+     }
+ 
+     public function decrementRecursionDepthAndPopLimit($byte_limit)
+diff --git a/php/tests/EncodeDecodeTest.php b/php/tests/EncodeDecodeTest.php
+index ddd01a1ac..e4889aefc 100644
+--- a/php/tests/EncodeDecodeTest.php
++++ b/php/tests/EncodeDecodeTest.php
+@@ -545,6 +545,31 @@ class EncodeDecodeTest extends TestBase
+         $m->mergeFromString(hex2bin("0afaffffff0f"));
+     }
+ 
++    private function makeRecursiveMessage($depth) {
++        $m = new TestMessage();
++        $m->setOptionalInt32(1);
++        if ($depth == 0) {
++            return $m;
++        }
++        $m->setRecursive($this->makeRecursiveMessage($depth - 1));
++        return $m;
++    }
++
++    public function testRecursiveMessage() {
++        $payload = $this->makeRecursiveMessage(99)->serializeToString();
++
++        $m = new TestMessage();
++        $m->mergeFromString($payload);
++    }
++
++    public function testOverlyRecursiveMessage() {
++        $this->expectException(Exception::class);
++        $payload = $this->makeRecursiveMessage(101)->serializeToString();
++
++        $m = new TestMessage();
++        $m->mergeFromString($payload);
++    }
++
+     public function testRandomFieldOrder()
+     {
+         $m = new TestRandomFieldOrder();
+-- 
+2.47.3
+
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254-1.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254-1.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254-1.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254-1.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,202 @@
+Backport of:
+
+From: Protobuf Team Bot <[email protected]>
+Date: Fri, 16 Aug 2024 16:57:47 -0700
+Subject: [PATCH 1/5] Internal change
+
+PiperOrigin-RevId: 663919912
+---
+ .../com/google/protobuf/CodedInputStream.java | 106 ++++--------------
+ .../com/google/protobuf/map_lite_test.proto   |   3 +-
+ .../proto/com/google/protobuf/map_test.proto  |   3 +-
+ .../java/com/google/protobuf/LiteTest.java    |   1 +
+ 4 files changed, 29 insertions(+), 84 deletions(-)
+
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
+@@ -246,13 +246,35 @@ public abstract class CodedInputStream {
+    * Reads and discards an entire message. This will read either until EOF or 
until an endgroup tag,
+    * whichever comes first.
+    */
+-  public abstract void skipMessage() throws IOException;
++  public void skipMessage() throws IOException {
++    while (true) {
++      final int tag = readTag();
++      if (tag == 0) {
++        return;
++      }
++      boolean fieldSkipped = skipField(tag);
++      if (!fieldSkipped) {
++        return;
++      }
++    }
++  }
+ 
+   /**
+    * Reads an entire message and writes it to output in wire format. This 
will read either until EOF
+    * or until an endgroup tag, whichever comes first.
+    */
+-  public abstract void skipMessage(CodedOutputStream output) throws 
IOException;
++  public void skipMessage(CodedOutputStream output) throws IOException {
++    while (true) {
++      final int tag = readTag();
++      if (tag == 0) {
++        return;
++      }
++      boolean fieldSkipped = skipField(tag, output);
++      if (!fieldSkipped) {
++        return;
++      }
++    }
++  }
+ 
+ 
+   // -----------------------------------------------------------------
+@@ -725,27 +747,6 @@ public abstract class CodedInputStream {
+       }
+     }
+ 
+-    @Override
+-    public void skipMessage() throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-    @Override
+-    public void skipMessage(CodedOutputStream output) throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag, output)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-
+     // -----------------------------------------------------------------
+ 
+     @Override
+@@ -1440,27 +1441,6 @@ public abstract class CodedInputStream {
+       }
+     }
+ 
+-    @Override
+-    public void skipMessage() throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-    @Override
+-    public void skipMessage(CodedOutputStream output) throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag, output)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-
+     // -----------------------------------------------------------------
+ 
+     @Override
+@@ -2208,26 +2188,6 @@ public abstract class CodedInputStream {
+       }
+     }
+ 
+-    @Override
+-    public void skipMessage() throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-    @Override
+-    public void skipMessage(CodedOutputStream output) throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag, output)) {
+-          return;
+-        }
+-      }
+-    }
+-
+     /** Collects the bytes skipped and returns the data in a ByteBuffer. */
+     private class SkippedDataSink implements RefillCallback {
+       private int lastPos = pos;
+@@ -3329,26 +3289,6 @@ public abstract class CodedInputStream {
+       }
+     }
+ 
+-    @Override
+-    public void skipMessage() throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag)) {
+-          return;
+-        }
+-      }
+-    }
+-
+-    @Override
+-    public void skipMessage(CodedOutputStream output) throws IOException {
+-      while (true) {
+-        final int tag = readTag();
+-        if (tag == 0 || !skipField(tag, output)) {
+-          return;
+-        }
+-      }
+-    }
+-
+     // -----------------------------------------------------------------
+ 
+     @Override
+Index: 
protobuf-3.21.12/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto
++++ 
protobuf-3.21.12/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto
+@@ -122,5 +122,6 @@ message ReservedAsMapFieldWithEnumValue
+ 
+ // https://github.com/protocolbuffers/protobuf/issues/9785
+ message MapContainer {
+-  map<string,string> my_map = 1;
++  map<string, string> my_map = 1;
++  map<uint32, string> m = 3;
+ }
+Index: 
protobuf-3.21.12/java/core/src/test/proto/com/google/protobuf/map_test.proto
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/test/proto/com/google/protobuf/map_test.proto
++++ 
protobuf-3.21.12/java/core/src/test/proto/com/google/protobuf/map_test.proto
+@@ -121,5 +121,6 @@ message ReservedAsMapFieldWithEnumValue
+ 
+ // https://github.com/protocolbuffers/protobuf/issues/9785
+ message MapContainer {
+-  map<string,string> my_map = 1;
++  map<string, string> my_map = 1;
++  map<uint32, string> m = 3;
+ }
+Index: 
protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/lite/src/test/java/com/google/protobuf/LiteTest.java
++++ protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+@@ -51,6 +51,7 @@ import com.google.protobuf.UnittestLite.
+ import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder;
+ import com.google.protobuf.UnittestLite.TestHugeFieldNumbersLite;
+ import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
++import map_lite_test.MapTestProto.MapContainer;
+ import map_lite_test.MapTestProto.TestMap;
+ import map_lite_test.MapTestProto.TestMap.MessageValue;
+ import protobuf_unittest.NestedExtensionLite;
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254-2.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254-2.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254-2.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254-2.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,143 @@
+From 850fcce9176e2c9070614dab53537760498c926b Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Thu, 18 Jul 2024 07:41:01 -0700
+Subject: [PATCH 2/5] Internal change
+
+PiperOrigin-RevId: 653615736
+---
+ .../core/src/main/java/com/google/protobuf/ArrayDecoders.java | 3 +--
+ .../com/google/protobuf/InvalidProtocolBufferException.java   | 2 +-
+ .../core/src/main/java/com/google/protobuf/MessageSchema.java | 3 +++
+ .../src/main/java/com/google/protobuf/MessageSetSchema.java   | 1 +
+ .../src/main/java/com/google/protobuf/UnknownFieldSchema.java | 3 +--
+ java/lite/src/test/java/com/google/protobuf/LiteTest.java     | 3 +++
+ src/google/protobuf/unittest_lite.proto                       | 4 ++++
+ 7 files changed, 14 insertions(+), 5 deletions(-)
+
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
+@@ -47,8 +47,7 @@ import java.io.IOException;
+ @CheckReturnValue
+ final class ArrayDecoders {
+ 
+-  private ArrayDecoders() {
+-  }
++  private ArrayDecoders() {}
+ 
+   /**
+    * A helper used to return multiple values in a Java function. Java doesn't 
natively support
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
+@@ -155,7 +155,7 @@ public class InvalidProtocolBufferExcept
+   static InvalidProtocolBufferException recursionLimitExceeded() {
+     return new InvalidProtocolBufferException(
+         "Protocol message had too many levels of nesting.  May be malicious.  
"
+-            + "Use CodedInputStream.setRecursionLimit() to increase the depth 
limit.");
++            + "Use setRecursionLimit() to increase the recursion depth 
limit.");
+   }
+ 
+   static InvalidProtocolBufferException sizeLimitExceeded() {
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/MessageSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSchema.java
+@@ -3946,6 +3946,7 @@ final class MessageSchema<T> implements
+               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+             }
+             // Unknown field.
++
+             if (unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
+               continue;
+             }
+@@ -4321,6 +4322,7 @@ final class MessageSchema<T> implements
+               if (unknownFields == null) {
+                 unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+               }
++
+               if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, 
reader)) {
+                 return;
+               }
+@@ -4337,6 +4339,7 @@ final class MessageSchema<T> implements
+             if (unknownFields == null) {
+               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+             }
++
+             if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) 
{
+               return;
+             }
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
+@@ -301,6 +301,7 @@ final class MessageSetSchema<T> implemen
+               reader, extension, extensionRegistry, extensions);
+           return true;
+         } else {
++
+           return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader);
+         }
+       } else {
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
+@@ -78,7 +78,6 @@ abstract class UnknownFieldSchema<T, B>
+   /** Marks unknown fields as immutable. */
+   abstract void makeImmutable(Object message);
+ 
+-  /** Merges one field into the unknown fields. */
+   final boolean mergeOneFieldFrom(B unknownFields, Reader reader) throws 
IOException {
+     int tag = reader.getTag();
+     int fieldNumber = WireFormat.getTagFieldNumber(tag);
+@@ -111,7 +110,7 @@ abstract class UnknownFieldSchema<T, B>
+     }
+   }
+ 
+-  final void mergeFrom(B unknownFields, Reader reader) throws IOException {
++  private final void mergeFrom(B unknownFields, Reader reader) throws 
IOException {
+     while (true) {
+       if (reader.getFieldNumber() == Reader.READ_DONE
+           || !mergeOneFieldFrom(unknownFields, reader)) {
+Index: 
protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/lite/src/test/java/com/google/protobuf/LiteTest.java
++++ protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+@@ -33,12 +33,14 @@ package com.google.protobuf;
+ import static com.google.common.truth.Truth.assertThat;
+ import static com.google.common.truth.Truth.assertWithMessage;
+ import static java.util.Collections.singletonList;
++import static org.junit.Assert.assertThrows;
+ 
+ import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
+ import com.google.protobuf.UnittestImportLite.ImportEnumLite;
+ import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
+ import com.google.protobuf.UnittestLite.ForeignEnumLite;
+ import com.google.protobuf.UnittestLite.ForeignMessageLite;
++import com.google.protobuf.UnittestLite.RecursiveGroup;
+ import com.google.protobuf.UnittestLite.RecursiveMessage;
+ import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+ import com.google.protobuf.UnittestLite.TestAllTypesLite;
+@@ -73,6 +75,7 @@ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Iterator;
+ import java.util.List;
++import java.util.concurrent.atomic.AtomicBoolean;
+ import org.junit.Before;
+ import org.junit.Test;
+ import org.junit.runner.RunWith;
+Index: protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+===================================================================
+--- protobuf-3.21.12.orig/src/google/protobuf/unittest_lite.proto
++++ protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+@@ -518,3 +518,7 @@ message RecursiveMessage {
+   optional RecursiveMessage recurse = 1;
+   optional bytes payload = 2;
+ }
++
++message RecursiveGroup {
++  RecursiveGroup recurse = 1 [features.message_encoding = DELIMITED];
++}
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254-3.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254-3.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254-3.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254-3.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,684 @@
+Backport of:
+
+From 4728531c162f2f9e8c2ca1add713cfee2db6be3b Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Tue, 17 Sep 2024 12:03:36 -0700
+Subject: [PATCH 3/5] Add recursion check when parsing unknown fields in Java.
+
+PiperOrigin-RevId: 675657198
+---
+ .../com/google/protobuf/ArrayDecoders.java    |  28 +++
+ .../com/google/protobuf/CodedInputStream.java |   6 +
+ .../com/google/protobuf/MessageSchema.java    |  12 +-
+ .../com/google/protobuf/MessageSetSchema.java |   3 +-
+ .../google/protobuf/UnknownFieldSchema.java   |  29 ++-
+ .../google/protobuf/CodedInputStreamTest.java | 158 ++++++++++++
+ .../java/com/google/protobuf/LiteTest.java    | 232 ++++++++++++++++++
+ 7 files changed, 456 insertions(+), 12 deletions(-)
+
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
+@@ -46,6 +46,10 @@ import java.io.IOException;
+  */
+ @CheckReturnValue
+ final class ArrayDecoders {
++  static final int DEFAULT_RECURSION_LIMIT = 100;
++
++  @SuppressWarnings("NonFinalStaticField")
++  private static volatile int recursionLimit = DEFAULT_RECURSION_LIMIT;
+ 
+   private ArrayDecoders() {}
+ 
+@@ -60,6 +64,7 @@ final class ArrayDecoders {
+     public long long1;
+     public Object object1;
+     public final ExtensionRegistryLite extensionRegistry;
++    public int recursionDepth;
+ 
+     Registers() {
+       this.extensionRegistry = ExtensionRegistryLite.getEmptyRegistry();
+@@ -267,7 +272,10 @@ final class ArrayDecoders {
+     if (length < 0 || length > limit - position) {
+       throw InvalidProtocolBufferException.truncatedMessage();
+     }
++    registers.recursionDepth++;
++    checkRecursionLimit(registers.recursionDepth);
+     schema.mergeFrom(msg, data, position, position + length, registers);
++    registers.recursionDepth--;
+     registers.object1 = msg;
+     return position + length;
+   }
+@@ -285,9 +293,12 @@ final class ArrayDecoders {
+     // A group field must has a MessageSchema (the only other subclass of 
Schema is MessageSetSchema
+     // and it can't be used in group fields).
+     final MessageSchema messageSchema = (MessageSchema) schema;
++    registers.recursionDepth++;
++    checkRecursionLimit(registers.recursionDepth);
+     // It's OK to directly use parseProto2Message since proto3 doesn't have 
group.
+     final int endPosition =
+         messageSchema.parseProto2Message(msg, data, position, limit, 
endGroup, registers);
++    registers.recursionDepth--;
+     registers.object1 = msg;
+     return endPosition;
+   }
+@@ -1048,6 +1059,8 @@ final class ArrayDecoders {
+         final UnknownFieldSetLite child = UnknownFieldSetLite.newInstance();
+         final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP;
+         int lastTag = 0;
++        registers.recursionDepth++;
++        checkRecursionLimit(registers.recursionDepth);
+         while (position < limit) {
+           position = decodeVarint32(data, position, registers);
+           lastTag = registers.int1;
+@@ -1056,6 +1069,7 @@ final class ArrayDecoders {
+           }
+           position = decodeUnknownField(lastTag, data, position, limit, 
child, registers);
+         }
++        registers.recursionDepth--;
+         if (position > limit || lastTag != endGroup) {
+           throw InvalidProtocolBufferException.parseFailure();
+         }
+@@ -1102,4 +1116,18 @@ final class ArrayDecoders {
+         throw InvalidProtocolBufferException.invalidTag();
+     }
+   }
++
++  /**
++   * Set the maximum recursion limit that ArrayDecoders will allow. An 
exception will be thrown if
++   * the depth of the message exceeds this limit.
++   */
++  public static void setRecursionLimit(int limit) {
++    recursionLimit = limit;
++  }
++
++  private static void checkRecursionLimit(int depth) throws 
InvalidProtocolBufferException {
++    if (depth >= recursionLimit) {
++      throw InvalidProtocolBufferException.recursionLimitExceeded();
++    }
++  }
+ }
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
+@@ -252,7 +252,10 @@ public abstract class CodedInputStream {
+       if (tag == 0) {
+         return;
+       }
++      checkRecursionLimit();
++      ++recursionDepth;
+       boolean fieldSkipped = skipField(tag);
++      --recursionDepth;
+       if (!fieldSkipped) {
+         return;
+       }
+@@ -269,7 +272,10 @@ public abstract class CodedInputStream {
+       if (tag == 0) {
+         return;
+       }
++      checkRecursionLimit();
++      ++recursionDepth;
+       boolean fieldSkipped = skipField(tag, output);
++      --recursionDepth;
+       if (!fieldSkipped) {
+         return;
+       }
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/MessageSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSchema.java
+@@ -3946,8 +3946,8 @@ final class MessageSchema<T> implements
+               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+             }
+             // Unknown field.
+-
+-            if (unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
++            if (unknownFieldSchema.mergeOneFieldFrom(
++                unknownFields, reader, /* currentDepth= */ 0)) {
+               continue;
+             }
+           }
+@@ -4322,8 +4322,8 @@ final class MessageSchema<T> implements
+               if (unknownFields == null) {
+                 unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+               }
+-
+-              if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, 
reader)) {
++              if (!unknownFieldSchema.mergeOneFieldFrom(
++                  unknownFields, reader, /* currentDepth= */ 0)) {
+                 return;
+               }
+               break;
+@@ -4339,8 +4339,8 @@ final class MessageSchema<T> implements
+             if (unknownFields == null) {
+               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
+             }
+-
+-            if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) 
{
++            if (!unknownFieldSchema.mergeOneFieldFrom(
++                unknownFields, reader, /* currentDepth= */ 0)) {
+               return;
+             }
+           }
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
+@@ -301,8 +301,7 @@ final class MessageSetSchema<T> implemen
+               reader, extension, extensionRegistry, extensions);
+           return true;
+         } else {
+-
+-          return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader);
++          return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader, 
/* currentDepth= */ 0);
+         }
+       } else {
+         return reader.skipField();
+Index: 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
++++ 
protobuf-3.21.12/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
+@@ -36,6 +36,11 @@ import java.io.IOException;
+ @CheckReturnValue
+ abstract class UnknownFieldSchema<T, B> {
+ 
++  static final int DEFAULT_RECURSION_LIMIT = 100;
++
++  @SuppressWarnings("NonFinalStaticField")
++  private static volatile int recursionLimit = DEFAULT_RECURSION_LIMIT;
++
+   /** Whether unknown fields should be dropped. */
+   abstract boolean shouldDiscardUnknownFields(Reader reader);
+ 
+@@ -78,7 +83,9 @@ abstract class UnknownFieldSchema<T, B>
+   /** Marks unknown fields as immutable. */
+   abstract void makeImmutable(Object message);
+ 
+-  final boolean mergeOneFieldFrom(B unknownFields, Reader reader) throws 
IOException {
++  /** Merges one field into the unknown fields. */
++  final boolean mergeOneFieldFrom(B unknownFields, Reader reader, int 
currentDepth)
++      throws IOException {
+     int tag = reader.getTag();
+     int fieldNumber = WireFormat.getTagFieldNumber(tag);
+     switch (WireFormat.getTagWireType(tag)) {
+@@ -97,7 +104,12 @@ abstract class UnknownFieldSchema<T, B>
+       case WireFormat.WIRETYPE_START_GROUP:
+         final B subFields = newBuilder();
+         int endGroupTag = WireFormat.makeTag(fieldNumber, 
WireFormat.WIRETYPE_END_GROUP);
+-        mergeFrom(subFields, reader);
++        currentDepth++;
++        if (currentDepth >= recursionLimit) {
++          throw InvalidProtocolBufferException.recursionLimitExceeded();
++        }
++        mergeFrom(subFields, reader, currentDepth);
++        currentDepth--;
+         if (endGroupTag != reader.getTag()) {
+           throw InvalidProtocolBufferException.invalidEndTag();
+         }
+@@ -110,10 +122,11 @@ abstract class UnknownFieldSchema<T, B>
+     }
+   }
+ 
+-  private final void mergeFrom(B unknownFields, Reader reader) throws 
IOException {
++  private final void mergeFrom(B unknownFields, Reader reader, int 
currentDepth)
++      throws IOException {
+     while (true) {
+       if (reader.getFieldNumber() == Reader.READ_DONE
+-          || !mergeOneFieldFrom(unknownFields, reader)) {
++          || !mergeOneFieldFrom(unknownFields, reader, currentDepth)) {
+         break;
+       }
+     }
+@@ -130,4 +143,12 @@ abstract class UnknownFieldSchema<T, B>
+   abstract int getSerializedSizeAsMessageSet(T message);
+ 
+   abstract int getSerializedSize(T unknowns);
++
++  /**
++   * Set the maximum recursion limit that ArrayDecoders will allow. An 
exception will be thrown if
++   * the depth of the message exceeds this limit.
++   */
++  public void setRecursionLimit(int limit) {
++    recursionLimit = limit;
++  }
+ }
+Index: 
protobuf-3.21.12/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
++++ 
protobuf-3.21.12/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+@@ -33,6 +33,9 @@ package com.google.protobuf;
+ import static com.google.common.truth.Truth.assertThat;
+ import static com.google.common.truth.Truth.assertWithMessage;
+ import static org.junit.Assert.assertArrayEquals;
++
++import com.google.common.primitives.Bytes;
++import map_test.MapTestProto.MapContainer;
+ import protobuf_unittest.UnittestProto.BoolMessage;
+ import protobuf_unittest.UnittestProto.Int32Message;
+ import protobuf_unittest.UnittestProto.Int64Message;
+@@ -57,6 +60,13 @@ public class CodedInputStreamTest {
+ 
+   private static final int DEFAULT_BLOCK_SIZE = 4096;
+ 
++  private static final int GROUP_TAP = WireFormat.makeTag(3, 
WireFormat.WIRETYPE_START_GROUP);
++
++  private static final byte[] NESTING_SGROUP = generateSGroupTags();
++
++  private static final byte[] NESTING_SGROUP_WITH_INITIAL_BYTES = 
generateSGroupTagsForMapField();
++
++
+   private enum InputType {
+     ARRAY {
+       @Override
+@@ -139,6 +149,17 @@ public class CodedInputStreamTest {
+     return bytes;
+   }
+ 
++  private static byte[] generateSGroupTags() {
++    byte[] bytes = new byte[100000];
++    Arrays.fill(bytes, (byte) GROUP_TAP);
++    return bytes;
++  }
++
++  private static byte[] generateSGroupTagsForMapField() {
++    byte[] initialBytes = {18, 1, 75, 26, (byte) 198, (byte) 154, 12};
++    return Bytes.concat(initialBytes, NESTING_SGROUP);
++  }
++
+   /**
+    * An InputStream which limits the number of bytes it reads at a time. We 
use this to make sure
+    * that CodedInputStream doesn't screw up when reading in small blocks.
+@@ -684,6 +705,143 @@ public class CodedInputStreamTest {
+     }
+   }
+ 
++  @Test
++  public void testMaliciousRecursion_unknownFields() throws Exception {
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> TestRecursiveMessage.parseFrom(NESTING_SGROUP));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousRecursion_skippingUnknownField() throws Exception {
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () ->
++                DiscardUnknownFieldsParser.wrap(TestRecursiveMessage.parser())
++                    .parseFrom(NESTING_SGROUP));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTagsWithMapField_fromInputStream() throws 
Exception {
++    Throwable parseFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () ->
++                MapContainer.parseFrom(
++                    new 
ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
++    Throwable mergeFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () ->
++                MapContainer.newBuilder()
++                    .mergeFrom(new 
ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
++
++    assertThat(parseFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++    assertThat(mergeFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTags_inputStream_skipMessage() throws 
Exception {
++    ByteArrayInputStream inputSteam = new 
ByteArrayInputStream(NESTING_SGROUP);
++    CodedInputStream input = CodedInputStream.newInstance(inputSteam);
++    CodedOutputStream output = CodedOutputStream.newInstance(new 
byte[NESTING_SGROUP.length]);
++
++    Throwable thrown = assertThrows(InvalidProtocolBufferException.class, 
input::skipMessage);
++    Throwable thrown2 =
++        assertThrows(InvalidProtocolBufferException.class, () -> 
input.skipMessage(output));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++    assertThat(thrown2)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws 
Exception {
++    Throwable parseFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> MapContainer.parseFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
++    Throwable mergeFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> 
MapContainer.newBuilder().mergeFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
++
++    assertThat(parseFromThrown)
++        .hasMessageThat()
++        .contains("the input ended unexpectedly in the middle of a field");
++    assertThat(mergeFromThrown)
++        .hasMessageThat()
++        .contains("the input ended unexpectedly in the middle of a field");
++  }
++
++  @Test
++  public void testMaliciousSGroupTags_arrayDecoder_skipMessage() throws 
Exception {
++    CodedInputStream input = CodedInputStream.newInstance(NESTING_SGROUP);
++    CodedOutputStream output = CodedOutputStream.newInstance(new 
byte[NESTING_SGROUP.length]);
++
++    Throwable thrown = assertThrows(InvalidProtocolBufferException.class, 
input::skipMessage);
++    Throwable thrown2 =
++        assertThrows(InvalidProtocolBufferException.class, () -> 
input.skipMessage(output));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++    assertThat(thrown2)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTagsWithMapField_fromByteBuffer() throws 
Exception {
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> 
MapContainer.parseFrom(ByteBuffer.wrap(NESTING_SGROUP_WITH_INITIAL_BYTES)));
++
++    assertThat(thrown)
++        .hasMessageThat()
++        .contains("the input ended unexpectedly in the middle of a field");
++  }
++
++  @Test
++  public void testMaliciousSGroupTags_byteBuffer_skipMessage() throws 
Exception {
++    CodedInputStream input = InputType.NIO_DIRECT.newDecoder(NESTING_SGROUP);
++    CodedOutputStream output = CodedOutputStream.newInstance(new 
byte[NESTING_SGROUP.length]);
++
++    Throwable thrown = assertThrows(InvalidProtocolBufferException.class, 
input::skipMessage);
++    Throwable thrown2 =
++        assertThrows(InvalidProtocolBufferException.class, () -> 
input.skipMessage(output));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++    assertThat(thrown2)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTags_iterableByteBuffer() throws Exception {
++    CodedInputStream input = InputType.ITER_DIRECT.newDecoder(NESTING_SGROUP);
++    CodedOutputStream output = CodedOutputStream.newInstance(new 
byte[NESTING_SGROUP.length]);
++
++    Throwable thrown = assertThrows(InvalidProtocolBufferException.class, 
input::skipMessage);
++    Throwable thrown2 =
++        assertThrows(InvalidProtocolBufferException.class, () -> 
input.skipMessage(output));
++
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++    assertThat(thrown2)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
+   private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
+     assertThat(e)
+         .hasMessageThat()
+Index: 
protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/lite/src/test/java/com/google/protobuf/LiteTest.java
++++ protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+@@ -2461,6 +2461,211 @@ public class LiteTest {
+   }
+ 
+   @Test
++  public void testParseFromInputStream_concurrent_nestingUnknownGroups() 
throws Exception {
++    int numThreads = 200;
++    ArrayList<Thread> threads = new ArrayList<>();
++
++    ByteString byteString = generateNestingGroups(99);
++    AtomicBoolean thrown = new AtomicBoolean(false);
++
++    for (int i = 0; i < numThreads; i++) {
++      Thread thread =
++          new Thread(
++              () -> {
++                try {
++                  TestAllTypesLite unused = 
TestAllTypesLite.parseFrom(byteString);
++                } catch (IOException e) {
++                  if (e.getMessage().contains("Protocol message had too many 
levels of nesting")) {
++                    thrown.set(true);
++                  }
++                }
++              });
++      thread.start();
++      threads.add(thread);
++    }
++
++    for (Thread thread : threads) {
++      thread.join();
++    }
++
++    assertThat(thrown.get()).isFalse();
++  }
++
++  @Test
++  public void testParseFromInputStream_nestingUnknownGroups() throws 
IOException {
++    ByteString byteString = generateNestingGroups(99);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class, () -> 
TestAllTypesLite.parseFrom(byteString));
++    assertThat(thrown)
++        .hasMessageThat()
++        .doesNotContain("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testParseFromInputStream_nestingUnknownGroups_exception() 
throws IOException {
++    ByteString byteString = generateNestingGroups(100);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class, () -> 
TestAllTypesLite.parseFrom(byteString));
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  public void testParseFromInputStream_setRecursionLimit_exception() throws 
IOException {
++    ByteString byteString = generateNestingGroups(199);
++    UnknownFieldSchema<?, ?> schema = SchemaUtil.unknownFieldSetLiteSchema();
++    schema.setRecursionLimit(200);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class, () -> 
TestAllTypesLite.parseFrom(byteString));
++    assertThat(thrown)
++        .hasMessageThat()
++        .doesNotContain("Protocol message had too many levels of nesting");
++    schema.setRecursionLimit(UnknownFieldSchema.DEFAULT_RECURSION_LIMIT);
++  }
++
++  @Test
++  public void testParseFromBytes_concurrent_nestingUnknownGroups() throws 
Exception {
++    int numThreads = 200;
++    ArrayList<Thread> threads = new ArrayList<>();
++
++    ByteString byteString = generateNestingGroups(99);
++    AtomicBoolean thrown = new AtomicBoolean(false);
++
++    for (int i = 0; i < numThreads; i++) {
++      Thread thread =
++          new Thread(
++              () -> {
++                try {
++                  // Should pass in byte[] instead of ByteString to go into 
ArrayDecoders.
++                  TestAllTypesLite unused = 
TestAllTypesLite.parseFrom(byteString.toByteArray());
++                } catch (InvalidProtocolBufferException e) {
++                  if (e.getMessage().contains("Protocol message had too many 
levels of nesting")) {
++                    thrown.set(true);
++                  }
++                }
++              });
++      thread.start();
++      threads.add(thread);
++    }
++
++    for (Thread thread : threads) {
++      thread.join();
++    }
++
++    assertThat(thrown.get()).isFalse();
++  }
++
++  @Test
++  public void testParseFromBytes_nestingUnknownGroups() throws IOException {
++    ByteString byteString = generateNestingGroups(99);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
++    assertThat(thrown)
++        .hasMessageThat()
++        .doesNotContain("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testParseFromBytes_nestingUnknownGroups_exception() throws 
IOException {
++    ByteString byteString = generateNestingGroups(100);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  public void testParseFromBytes_setRecursionLimit_exception() throws 
IOException {
++    ByteString byteString = generateNestingGroups(199);
++    ArrayDecoders.setRecursionLimit(200);
++
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
++    assertThat(thrown)
++        .hasMessageThat()
++        .doesNotContain("Protocol message had too many levels of nesting");
++    ArrayDecoders.setRecursionLimit(ArrayDecoders.DEFAULT_RECURSION_LIMIT);
++  }
++
++  @Test
++  public void testParseFromBytes_recursiveMessages() throws Exception {
++    byte[] data99 = makeRecursiveMessage(99).toByteArray();
++    byte[] data100 = makeRecursiveMessage(100).toByteArray();
++
++    RecursiveMessage unused = RecursiveMessage.parseFrom(data99);
++    Throwable thrown =
++        assertThrows(
++            InvalidProtocolBufferException.class, () -> 
RecursiveMessage.parseFrom(data100));
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  public void testParseFromBytes_recursiveKnownGroups() throws Exception {
++    byte[] data99 = makeRecursiveGroup(99).toByteArray();
++    byte[] data100 = makeRecursiveGroup(100).toByteArray();
++
++    RecursiveGroup unused = RecursiveGroup.parseFrom(data99);
++    Throwable thrown =
++        assertThrows(InvalidProtocolBufferException.class, () -> 
RecursiveGroup.parseFrom(data100));
++    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
++  }
++
++  @Test
++  @SuppressWarnings("ProtoParseFromByteString")
++  public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws 
Exception {
++    ByteString byteString = generateNestingGroups(102);
++
++    Throwable parseFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> MapContainer.parseFrom(byteString.toByteArray()));
++    Throwable mergeFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> 
MapContainer.newBuilder().mergeFrom(byteString.toByteArray()));
++
++    assertThat(parseFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++    assertThat(mergeFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
++  public void testMaliciousSGroupTagsWithMapField_fromInputStream() throws 
Exception {
++    byte[] bytes = generateNestingGroups(101).toByteArray();
++
++    Throwable parseFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> MapContainer.parseFrom(new ByteArrayInputStream(bytes)));
++    Throwable mergeFromThrown =
++        assertThrows(
++            InvalidProtocolBufferException.class,
++            () -> MapContainer.newBuilder().mergeFrom(new 
ByteArrayInputStream(bytes)));
++
++    assertThat(parseFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++    assertThat(mergeFromThrown)
++        .hasMessageThat()
++        .contains("Protocol message had too many levels of nesting");
++  }
++
++  @Test
+   public void testParseFromByteBuffer_extensions() throws Exception {
+     TestAllExtensionsLite message =
+         TestAllExtensionsLite.newBuilder()
+@@ -2816,4 +3021,31 @@ public class LiteTest {
+     }
+     return false;
+   }
++
++  private static ByteString generateNestingGroups(int num) throws IOException 
{
++    int groupTap = WireFormat.makeTag(3, WireFormat.WIRETYPE_START_GROUP);
++    ByteString.Output byteStringOutput = ByteString.newOutput();
++    CodedOutputStream codedOutput = 
CodedOutputStream.newInstance(byteStringOutput);
++    for (int i = 0; i < num; i++) {
++      codedOutput.writeInt32NoTag(groupTap);
++    }
++    codedOutput.flush();
++    return byteStringOutput.toByteString();
++  }
++
++  private static RecursiveMessage makeRecursiveMessage(int num) {
++    if (num == 0) {
++      return RecursiveMessage.getDefaultInstance();
++    } else {
++      return 
RecursiveMessage.newBuilder().setRecurse(makeRecursiveMessage(num - 1)).build();
++    }
++  }
++
++  private static RecursiveGroup makeRecursiveGroup(int num) {
++    if (num == 0) {
++      return RecursiveGroup.getDefaultInstance();
++    } else {
++      return RecursiveGroup.newBuilder().setRecurse(makeRecursiveGroup(num - 
1)).build();
++    }
++  }
+ }
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254-4.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254-4.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254-4.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254-4.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,21 @@
+From f000b7e18fd6921ca02ea4b87608e8cadcb7b64f Mon Sep 17 00:00:00 2001
+From: Sandy Zhang <[email protected]>
+Date: Wed, 18 Sep 2024 14:57:16 -0400
+Subject: [PATCH 4/5] Fix merge conflict by adding optional label to proto2
+ unittest_lite.proto
+
+---
+ src/google/protobuf/unittest_lite.proto | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+===================================================================
+--- protobuf-3.21.12.orig/src/google/protobuf/unittest_lite.proto
++++ protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+@@ -520,5 +520,5 @@ message RecursiveMessage {
+ }
+ 
+ message RecursiveGroup {
+-  RecursiveGroup recurse = 1 [features.message_encoding = DELIMITED];
++  optional RecursiveGroup recurse = 1 [features.message_encoding = DELIMITED];
+ }
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254-5.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254-5.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254-5.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254-5.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,66 @@
+From b5a7cf7cf4b7e39f6b02205e45afe2104a7faf81 Mon Sep 17 00:00:00 2001
+From: Sandy Zhang <[email protected]>
+Date: Wed, 18 Sep 2024 15:25:25 -0400
+Subject: [PATCH 5/5] Remove RecursiveGroup test case which doesn't exist in
+ 25.x pre-Editions
+
+---
+ .../java/com/google/protobuf/LiteTest.java    | 20 -------------------
+ src/google/protobuf/unittest_lite.proto       |  4 ----
+ 2 files changed, 24 deletions(-)
+
+Index: 
protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+===================================================================
+--- 
protobuf-3.21.12.orig/java/lite/src/test/java/com/google/protobuf/LiteTest.java
++++ protobuf-3.21.12/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+@@ -40,7 +40,6 @@ import com.google.protobuf.UnittestImpor
+ import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
+ import com.google.protobuf.UnittestLite.ForeignEnumLite;
+ import com.google.protobuf.UnittestLite.ForeignMessageLite;
+-import com.google.protobuf.UnittestLite.RecursiveGroup;
+ import com.google.protobuf.UnittestLite.RecursiveMessage;
+ import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+ import com.google.protobuf.UnittestLite.TestAllTypesLite;
+@@ -2612,17 +2611,6 @@ public class LiteTest {
+   }
+ 
+   @Test
+-  public void testParseFromBytes_recursiveKnownGroups() throws Exception {
+-    byte[] data99 = makeRecursiveGroup(99).toByteArray();
+-    byte[] data100 = makeRecursiveGroup(100).toByteArray();
+-
+-    RecursiveGroup unused = RecursiveGroup.parseFrom(data99);
+-    Throwable thrown =
+-        assertThrows(InvalidProtocolBufferException.class, () -> 
RecursiveGroup.parseFrom(data100));
+-    assertThat(thrown).hasMessageThat().contains("Protocol message had too 
many levels of nesting");
+-  }
+-
+-  @Test
+   @SuppressWarnings("ProtoParseFromByteString")
+   public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws 
Exception {
+     ByteString byteString = generateNestingGroups(102);
+@@ -3040,12 +3028,4 @@ public class LiteTest {
+       return 
RecursiveMessage.newBuilder().setRecurse(makeRecursiveMessage(num - 1)).build();
+     }
+   }
+-
+-  private static RecursiveGroup makeRecursiveGroup(int num) {
+-    if (num == 0) {
+-      return RecursiveGroup.getDefaultInstance();
+-    } else {
+-      return RecursiveGroup.newBuilder().setRecurse(makeRecursiveGroup(num - 
1)).build();
+-    }
+-  }
+ }
+Index: protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+===================================================================
+--- protobuf-3.21.12.orig/src/google/protobuf/unittest_lite.proto
++++ protobuf-3.21.12/src/google/protobuf/unittest_lite.proto
+@@ -518,7 +518,3 @@ message RecursiveMessage {
+   optional RecursiveMessage recurse = 1;
+   optional bytes payload = 2;
+ }
+-
+-message RecursiveGroup {
+-  optional RecursiveGroup recurse = 1 [features.message_encoding = DELIMITED];
+-}
diff -Nru protobuf-3.21.12/debian/patches/CVE-2024-7254.patch 
protobuf-3.21.12/debian/patches/CVE-2024-7254.patch
--- protobuf-3.21.12/debian/patches/CVE-2024-7254.patch 2025-04-29 
22:27:02.000000000 +0300
+++ protobuf-3.21.12/debian/patches/CVE-2024-7254.patch 1970-01-01 
02:00:00.000000000 +0200
@@ -1,143 +0,0 @@
-From cc8b3483a5584b3301e3d43d17eb59704857ffaa Mon Sep 17 00:00:00 2001
-From: Protobuf Team Bot <[email protected]>
-Date: Thu, 18 Jul 2024 07:41:01 -0700
-Subject: [PATCH] Internal change
-
-PiperOrigin-RevId: 653615736
----
- .../core/src/main/java/com/google/protobuf/ArrayDecoders.java | 3 +--
- .../com/google/protobuf/InvalidProtocolBufferException.java   | 2 +-
- .../core/src/main/java/com/google/protobuf/MessageSchema.java | 3 +++
- .../src/main/java/com/google/protobuf/MessageSetSchema.java   | 1 +
- .../src/main/java/com/google/protobuf/UnknownFieldSchema.java | 3 +--
- java/lite/src/test/java/com/google/protobuf/LiteTest.java     | 3 +++
- src/google/protobuf/unittest_lite.proto                       | 4 ++++
- 7 files changed, 14 insertions(+), 5 deletions(-)
-
-diff --git a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java 
b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
-index f3241de5095c0..9bf14396263a3 100644
---- a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
-+++ b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
-@@ -47,8 +47,7 @@
- @CheckReturnValue
- final class ArrayDecoders {
- 
--  private ArrayDecoders() {
--  }
-+  private ArrayDecoders() {}
- 
-   /**
-    * A helper used to return multiple values in a Java function. Java doesn't 
natively support
-diff --git 
a/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
 
b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
-index 5d10e48884c1a..dbcb9e899dbee 100644
---- 
a/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
-+++ 
b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
-@@ -155,7 +155,7 @@ public InvalidWireTypeException(String description) {
-   static InvalidProtocolBufferException recursionLimitExceeded() {
-     return new InvalidProtocolBufferException(
-         "Protocol message had too many levels of nesting.  May be malicious.  
"
--            + "Use CodedInputStream.setRecursionLimit() to increase the depth 
limit.");
-+            + "Use setRecursionLimit() to increase the recursion depth 
limit.");
-   }
- 
-   static InvalidProtocolBufferException sizeLimitExceeded() {
-diff --git a/java/core/src/main/java/com/google/protobuf/MessageSchema.java 
b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
-index de3890f7023aa..f8f79fcdf8b4c 100644
---- a/java/core/src/main/java/com/google/protobuf/MessageSchema.java
-+++ b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
-@@ -3946,6 +3946,7 @@ private <UT, UB, ET extends FieldDescriptorLite<ET>> 
void mergeFromHelper(
-               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
-             }
-             // Unknown field.
-+
-             if (unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
-               continue;
-             }
-@@ -4321,6 +4322,7 @@ private <UT, UB, ET extends FieldDescriptorLite<ET>> 
void mergeFromHelper(
-               if (unknownFields == null) {
-                 unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
-               }
-+
-               if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, 
reader)) {
-                 return;
-               }
-@@ -4337,6 +4339,7 @@ private <UT, UB, ET extends FieldDescriptorLite<ET>> 
void mergeFromHelper(
-             if (unknownFields == null) {
-               unknownFields = 
unknownFieldSchema.getBuilderFromMessage(message);
-             }
-+
-             if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) 
{
-               return;
-             }
-diff --git a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java 
b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
-index eec3acd35ca79..a17037e8efd4e 100644
---- a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
-+++ b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
-@@ -301,6 +301,7 @@ boolean parseMessageSetItemOrUnknownField(
-               reader, extension, extensionRegistry, extensions);
-           return true;
-         } else {
-+
-           return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader);
-         }
-       } else {
-diff --git 
a/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java 
b/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
-index c4ec645bf7fc8..a43bc2a9472d3 100644
---- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
-+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
-@@ -78,7 +78,6 @@ abstract class UnknownFieldSchema<T, B> {
-   /** Marks unknown fields as immutable. */
-   abstract void makeImmutable(Object message);
- 
--  /** Merges one field into the unknown fields. */
-   final boolean mergeOneFieldFrom(B unknownFields, Reader reader) throws 
IOException {
-     int tag = reader.getTag();
-     int fieldNumber = WireFormat.getTagFieldNumber(tag);
-@@ -111,7 +110,7 @@ final boolean mergeOneFieldFrom(B unknownFields, Reader 
reader) throws IOExcepti
-     }
-   }
- 
--  final void mergeFrom(B unknownFields, Reader reader) throws IOException {
-+  private final void mergeFrom(B unknownFields, Reader reader) throws 
IOException {
-     while (true) {
-       if (reader.getFieldNumber() == Reader.READ_DONE
-           || !mergeOneFieldFrom(unknownFields, reader)) {
-diff --git a/java/lite/src/test/java/com/google/protobuf/LiteTest.java 
b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
-index 0c7b8b535b18d..411bc63f087fd 100644
---- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java
-+++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
-@@ -33,12 +33,14 @@
- import static com.google.common.truth.Truth.assertThat;
- import static com.google.common.truth.Truth.assertWithMessage;
- import static java.util.Collections.singletonList;
-+import static org.junit.Assert.assertThrows;
- 
- import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
- import com.google.protobuf.UnittestImportLite.ImportEnumLite;
- import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
- import com.google.protobuf.UnittestLite.ForeignEnumLite;
- import com.google.protobuf.UnittestLite.ForeignMessageLite;
-+import com.google.protobuf.UnittestLite.RecursiveGroup;
- import com.google.protobuf.UnittestLite.RecursiveMessage;
- import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
- import com.google.protobuf.UnittestLite.TestAllTypesLite;
-@@ -72,6 +74,7 @@
- import java.util.Arrays;
- import java.util.Iterator;
- import java.util.List;
-+import java.util.concurrent.atomic.AtomicBoolean;
- import org.junit.Before;
- import org.junit.Test;
- import org.junit.runner.RunWith;
-diff --git a/src/google/protobuf/unittest_lite.proto 
b/src/google/protobuf/unittest_lite.proto
-index b3fcfa431c00a..4bc78c4de6a80 100644
---- a/src/google/protobuf/unittest_lite.proto
-+++ b/src/google/protobuf/unittest_lite.proto
-@@ -518,3 +518,7 @@ message RecursiveMessage {
-   optional RecursiveMessage recurse = 1;
-   optional bytes payload = 2;
- }
-+
-+message RecursiveGroup {
-+  optional RecursiveGroup recurse = 1;
-+}
diff -Nru protobuf-3.21.12/debian/patches/CVE-2025-4565-1.patch 
protobuf-3.21.12/debian/patches/CVE-2025-4565-1.patch
--- protobuf-3.21.12/debian/patches/CVE-2025-4565-1.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2025-4565-1.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,117 @@
+From f69ea1cc48191534c3450a1053f3741250274eca Mon Sep 17 00:00:00 2001
+From: Mike Kruskal <[email protected]>
+Date: Mon, 9 Dec 2024 20:04:43 -0800
+Subject: [PATCH] Make Pure Python reject unmatched end-group tag in unknown
+ fields
+
+This brings it into conformance with our spec and other languages.
+
+PiperOrigin-RevId: 704518974
+
+Origin: upstream, 
https://github.com/protocolbuffers/protobuf/commit/f69ea1cc48191534c3450a1053f3741250274eca.patch
+Bug: https://www.cve.org/CVERecord?id=CVE-2025-4565
+Bug-Debian: https://bugs.debian.org/1108057
+---
+ conformance/failure_list_python.txt           |  3 -
+ python/google/protobuf/internal/decoder.py    | 14 ++++-
+ .../google/protobuf/internal/decoder_test.py  | 56 ++++++++++++++++++-
+ .../google/protobuf/internal/message_test.py  | 31 ++++++----
+ .../protobuf/internal/python_message.py       |  3 +-
+ python/google/protobuf/unknown_fields.py      |  3 +-
+ 6 files changed, 90 insertions(+), 20 deletions(-)
+
+diff --git a/python/google/protobuf/internal/decoder.py 
b/python/google/protobuf/internal/decoder.py
+index dcde1d942..e26770e1d 100755
+--- a/python/google/protobuf/internal/decoder.py
++++ b/python/google/protobuf/internal/decoder.py
+@@ -976,14 +976,16 @@ def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
+     field_number, wire_type = wire_format.UnpackTag(tag)
+     if wire_type == wire_format.WIRETYPE_END_GROUP:
+       break
+-    (data, pos) = _DecodeUnknownField(buffer, pos, wire_type)
++    (data, pos) = _DecodeUnknownField(
++        buffer, pos, end_pos, field_number, wire_type
++    )
+     # pylint: disable=protected-access
+     unknown_field_set._add(field_number, wire_type, data)
+ 
+   return (unknown_field_set, pos)
+ 
+ 
+-def _DecodeUnknownField(buffer, pos, wire_type):
++def _DecodeUnknownField(buffer, pos, end_pos, field_number, wire_type):
+   """Decode a unknown field.  Returns the UnknownField and new position."""
+ 
+   if wire_type == wire_format.WIRETYPE_VARINT:
+@@ -997,7 +999,13 @@ def _DecodeUnknownField(buffer, pos, wire_type):
+     data = buffer[pos:pos+size].tobytes()
+     pos += size
+   elif wire_type == wire_format.WIRETYPE_START_GROUP:
+-    (data, pos) = _DecodeUnknownFieldSet(buffer, pos)
++    end_tag_bytes = encoder.TagBytes(
++        field_number, wire_format.WIRETYPE_END_GROUP
++    )
++    data, pos = _DecodeUnknownFieldSet(buffer, pos, end_pos)
++    # Check end tag.
++    if buffer[pos - len(end_tag_bytes) : pos] != end_tag_bytes:
++      raise _DecodeError('Missing group end tag.')
+   elif wire_type == wire_format.WIRETYPE_END_GROUP:
+     return (0, -1)
+   else:
+diff --git a/python/google/protobuf/internal/message_test.py 
b/python/google/protobuf/internal/message_test.py
+index f1541f14b..7212639db 100755
+--- a/python/google/protobuf/internal/message_test.py
++++ b/python/google/protobuf/internal/message_test.py
+@@ -122,6 +122,8 @@ class MessageTest(unittest.TestCase):
+     msg = message_module.TestAllTypes()
+     self.assertRaises(TypeError, msg.FromString, 0)
+     self.assertRaises(Exception, msg.FromString, '0')
++
++    # Unexpected end group tag.
+     # TODO(jieluo): Fix cpp extension to raise error instead of warning.
+     # b/27494216
+     end_tag = encoder.TagBytes(1, 4)
+@@ -133,6 +133,15 @@ class MessageTest(unittest.TestCase):
+         # Only pure-Python has an error message this specific.
+         self.assertEqual('Unexpected end-group tag.', str(context.exception))
+ 
++    # Unmatched start group tag.
++    start_tag = encoder.TagBytes(2, 3)
++    with self.assertRaises(message.DecodeError):
++      msg.FromString(start_tag)
++
++    # Mismatched end group tag.
++    with self.assertRaises(message.DecodeError):
++      msg.FromString(start_tag + end_tag)
++
+     # Field number 0 is illegal.
+     self.assertRaises(message.DecodeError, msg.FromString, b'\3\4')
+ 
+diff --git a/python/google/protobuf/internal/python_message.py 
b/python/google/protobuf/internal/python_message.py
+index 1f9016880..22d02c4d6 100755
+--- a/python/google/protobuf/internal/python_message.py
++++ b/python/google/protobuf/internal/python_message.py
+@@ -1179,7 +1179,8 @@ def _AddMergeFromStringMethod(message_de
+         # TODO(jieluo): remove old_pos.
+         old_pos = new_pos
+         (data, new_pos) = decoder._DecodeUnknownField(
+-            buffer, new_pos, wire_type)  # pylint: disable=protected-access
++            buffer, new_pos, end, field_number, wire_type
++        )  # pylint: disable=protected-access
+         if new_pos == -1:
+           return pos
+         # pylint: disable=protected-access
+diff --git a/python/google/protobuf/unknown_fields.py 
b/python/google/protobuf/unknown_fields.py
+index 9b1e54932..1a4537e3f 100644
+--- a/python/google/protobuf/unknown_fields.py
++++ b/python/google/protobuf/unknown_fields.py
+@@ -101,7 +101,8 @@ else:
+           if field_number == 0:
+             raise RuntimeError('Field number 0 is illegal.')
+           (data, _) = decoder._DecodeUnknownField(
+-              memoryview(buffer), 0, wire_type)
++              memoryview(buffer), 0, len(buffer), field_number, wire_type
++          )
+           InternalAdd(field_number, wire_type, data)
+ 
+     def __getitem__(self, index):
diff -Nru protobuf-3.21.12/debian/patches/CVE-2025-4565-2.patch 
protobuf-3.21.12/debian/patches/CVE-2025-4565-2.patch
--- protobuf-3.21.12/debian/patches/CVE-2025-4565-2.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2025-4565-2.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,395 @@
+From a6fa5799ff0153e4f0f6f1d8263d6b492ac129e0 Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Tue, 4 Mar 2025 13:16:32 -0800
+Subject: [PATCH] Internal pure python fixes
+
+PiperOrigin-RevId: 733441339
+
+Origin: upstream, 
https://github.com/protocolbuffers/protobuf/commit/a6fa5799ff0153e4f0f6f1d8263d6b492ac129e0.patch
+Bug: https://www.cve.org/CVERecord?id=CVE-2025-4565
+Bug-Debian: https://bugs.debian.org/1108057
+---
+ python/google/protobuf/internal/decoder.py    | 98 ++++++++++++++-----
+ .../google/protobuf/internal/message_test.py  |  1 +
+ .../protobuf/internal/python_message.py       |  7 +-
+ .../protobuf/internal/self_recursive.proto    |  9 +-
+ 4 files changed, 86 insertions(+), 29 deletions(-)
+
+diff --git a/python/google/protobuf/internal/decoder.py 
b/python/google/protobuf/internal/decoder.py
+index 91a4c7541e3dc..e2a3de17089ab 100755
+--- a/python/google/protobuf/internal/decoder.py
++++ b/python/google/protobuf/internal/decoder.py
+@@ -195,7 +195,10 @@ def SpecificDecoder(field_number, is_repeated, is_packed, 
key, new_default,
+                       clear_if_default=False):
+     if is_packed:
+       local_DecodeVarint = _DecodeVarint
+-      def DecodePackedField(buffer, pos, end, message, field_dict):
++      def DecodePackedField(
++          buffer, pos, end, message, field_dict, current_depth=0
++      ):
++        del current_depth  # unused
+         value = field_dict.get(key)
+         if value is None:
+           value = field_dict.setdefault(key, new_default(message))
+@@ -210,11 +213,15 @@ def DecodePackedField(buffer, pos, end, message, 
field_dict):
+           del value[-1]   # Discard corrupt value.
+           raise _DecodeError('Packed element was truncated.')
+         return pos
++
+       return DecodePackedField
+     elif is_repeated:
+       tag_bytes = encoder.TagBytes(field_number, wire_type)
+       tag_len = len(tag_bytes)
+-      def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++      def DecodeRepeatedField(
++          buffer, pos, end, message, field_dict, current_depth=0
++      ):
++        del current_depth  # unused
+         value = field_dict.get(key)
+         if value is None:
+           value = field_dict.setdefault(key, new_default(message))
+@@ -229,9 +236,12 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+             if new_pos > end:
+               raise _DecodeError('Truncated message.')
+             return new_pos
++
+       return DecodeRepeatedField
+     else:
+-      def DecodeField(buffer, pos, end, message, field_dict):
++
++      def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
++        del current_depth  # unused
+         (new_value, pos) = decode_value(buffer, pos)
+         if pos > end:
+           raise _DecodeError('Truncated message.')
+@@ -240,6 +250,7 @@ def DecodeField(buffer, pos, end, message, field_dict):
+         else:
+           field_dict[key] = new_value
+         return pos
++
+       return DecodeField
+ 
+   return SpecificDecoder
+@@ -375,7 +386,9 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, 
new_default,
+   enum_type = key.enum_type
+   if is_packed:
+     local_DecodeVarint = _DecodeVarint
+-    def DecodePackedField(buffer, pos, end, message, field_dict):
++    def DecodePackedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
+       """Decode serialized packed enum to its value and a new position.
+ 
+       Args:
+@@ -388,6 +401,7 @@ def DecodePackedField(buffer, pos, end, message, 
field_dict):
+       Returns:
+         int, new position in serialized data.
+       """
++      del current_depth  # unused
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -424,11 +438,14 @@ def DecodePackedField(buffer, pos, end, message, 
field_dict):
+           # pylint: enable=protected-access
+         raise _DecodeError('Packed element was truncated.')
+       return pos
++
+     return DecodePackedField
+   elif is_repeated:
+     tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT)
+     tag_len = len(tag_bytes)
+-    def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++    def DecodeRepeatedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
+       """Decode serialized repeated enum to its value and a new position.
+ 
+       Args:
+@@ -441,6 +458,7 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+       Returns:
+         int, new position in serialized data.
+       """
++      del current_depth  # unused
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -467,9 +485,11 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+           if new_pos > end:
+             raise _DecodeError('Truncated message.')
+           return new_pos
++
+     return DecodeRepeatedField
+   else:
+-    def DecodeField(buffer, pos, end, message, field_dict):
++
++    def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
+       """Decode serialized repeated enum to its value and a new position.
+ 
+       Args:
+@@ -482,6 +502,7 @@ def DecodeField(buffer, pos, end, message, field_dict):
+       Returns:
+         int, new position in serialized data.
+       """
++      del current_depth  # unused
+       value_start_pos = pos
+       (enum_value, pos) = _DecodeSignedVarint32(buffer, pos)
+       if pos > end:
+@@ -505,6 +505,7 @@ def EnumDecoder(field_number, is_repeate
+             field_number, wire_format.WIRETYPE_VARINT, enum_value)
+         # pylint: enable=protected-access
+       return pos
++
+     return DecodeField
+ 
+ 
+@@ -563,7 +585,10 @@ def _ConvertToUnicode(memview):
+     tag_bytes = encoder.TagBytes(field_number,
+                                  wire_format.WIRETYPE_LENGTH_DELIMITED)
+     tag_len = len(tag_bytes)
+-    def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++    def DecodeRepeatedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
++      del current_depth  # unused
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -578,9 +603,12 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if buffer[new_pos:pos] != tag_bytes or new_pos == end:
+           # Prediction failed.  Return.
+           return new_pos
++
+     return DecodeRepeatedField
+   else:
+-    def DecodeField(buffer, pos, end, message, field_dict):
++
++    def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
++      del current_depth  # unused
+       (size, pos) = local_DecodeVarint(buffer, pos)
+       new_pos = pos + size
+       if new_pos > end:
+@@ -590,6 +618,7 @@ def DecodeField(buffer, pos, end, message, field_dict):
+       else:
+         field_dict[key] = _ConvertToUnicode(buffer[pos:new_pos])
+       return new_pos
++
+     return DecodeField
+ 
+ 
+@@ -604,7 +633,10 @@ def BytesDecoder(field_number, is_repeated, is_packed, 
key, new_default,
+     tag_bytes = encoder.TagBytes(field_number,
+                                  wire_format.WIRETYPE_LENGTH_DELIMITED)
+     tag_len = len(tag_bytes)
+-    def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++    def DecodeRepeatedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
++      del current_depth  # unused
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -619,9 +651,12 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if buffer[new_pos:pos] != tag_bytes or new_pos == end:
+           # Prediction failed.  Return.
+           return new_pos
++
+     return DecodeRepeatedField
+   else:
+-    def DecodeField(buffer, pos, end, message, field_dict):
++
++    def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
++      del current_depth  # unused
+       (size, pos) = local_DecodeVarint(buffer, pos)
+       new_pos = pos + size
+       if new_pos > end:
+@@ -631,6 +666,7 @@ def DecodeField(buffer, pos, end, message, field_dict):
+       else:
+         field_dict[key] = buffer[pos:new_pos].tobytes()
+       return new_pos
++
+     return DecodeField
+ 
+ 
+@@ -646,7 +682,9 @@ def GroupDecoder(field_number, is_repeated, is_packed, 
key, new_default):
+     tag_bytes = encoder.TagBytes(field_number,
+                                  wire_format.WIRETYPE_START_GROUP)
+     tag_len = len(tag_bytes)
+-    def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++    def DecodeRepeatedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -655,7 +693,7 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if value is None:
+           value = field_dict.setdefault(key, new_default(message))
+         # Read sub-message.
+-        pos = value.add()._InternalParse(buffer, pos, end)
++        pos = value.add()._InternalParse(buffer, pos, end, current_depth)
+         # Read end tag.
+         new_pos = pos+end_tag_len
+         if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
+@@ -665,19 +703,22 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if buffer[new_pos:pos] != tag_bytes or new_pos == end:
+           # Prediction failed.  Return.
+           return new_pos
++
+     return DecodeRepeatedField
+   else:
+-    def DecodeField(buffer, pos, end, message, field_dict):
++
++    def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+       # Read sub-message.
+-      pos = value._InternalParse(buffer, pos, end)
++      pos = value._InternalParse(buffer, pos, end, current_depth)
+       # Read end tag.
+       new_pos = pos+end_tag_len
+       if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
+         raise _DecodeError('Missing group end tag.')
+       return new_pos
++
+     return DecodeField
+ 
+ 
+@@ -691,7 +732,9 @@ def MessageDecoder(field_number, is_repeated, is_packed, 
key, new_default):
+     tag_bytes = encoder.TagBytes(field_number,
+                                  wire_format.WIRETYPE_LENGTH_DELIMITED)
+     tag_len = len(tag_bytes)
+-    def DecodeRepeatedField(buffer, pos, end, message, field_dict):
++    def DecodeRepeatedField(
++        buffer, pos, end, message, field_dict, current_depth=0
++    ):
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -702,7 +745,10 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if new_pos > end:
+           raise _DecodeError('Truncated message.')
+         # Read sub-message.
+-        if value.add()._InternalParse(buffer, pos, new_pos) != new_pos:
++        if (
++            value.add()._InternalParse(buffer, pos, new_pos, current_depth)
++            != new_pos
++        ):
+           # The only reason _InternalParse would return early is if it
+           # encountered an end-group tag.
+           raise _DecodeError('Unexpected end-group tag.')
+@@ -711,9 +757,11 @@ def DecodeRepeatedField(buffer, pos, end, message, 
field_dict):
+         if buffer[new_pos:pos] != tag_bytes or new_pos == end:
+           # Prediction failed.  Return.
+           return new_pos
++
+     return DecodeRepeatedField
+   else:
+-    def DecodeField(buffer, pos, end, message, field_dict):
++
++    def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
+       value = field_dict.get(key)
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+@@ -723,11 +771,12 @@ def DecodeField(buffer, pos, end, message, field_dict):
+       if new_pos > end:
+         raise _DecodeError('Truncated message.')
+       # Read sub-message.
+-      if value._InternalParse(buffer, pos, new_pos) != new_pos:
++      if value._InternalParse(buffer, pos, new_pos, current_depth) != new_pos:
+         # The only reason _InternalParse would return early is if it 
encountered
+         # an end-group tag.
+         raise _DecodeError('Unexpected end-group tag.')
+       return new_pos
++
+     return DecodeField
+ 
+ 
+@@ -883,7 +932,8 @@ def MapDecoder(field_descriptor, new_default, 
is_message_map):
+   # Can't read _concrete_class yet; might not be initialized.
+   message_type = field_descriptor.message_type
+ 
+-  def DecodeMap(buffer, pos, end, message, field_dict):
++  def DecodeMap(buffer, pos, end, message, field_dict, current_depth=0):
++    del current_depth  # Unused.
+     submsg = message_type._concrete_class()
+     value = field_dict.get(key)
+     if value is None:
+@@ -966,7 +966,7 @@ def _SkipGroup(buffer, pos, end):
+     pos = new_pos
+ 
+ 
+-def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
++def _DecodeUnknownFieldSet(buffer, pos, end_pos=None, current_depth=0):
+   """Decode UnknownFieldSet.  Returns the UnknownFieldSet and new position."""
+ 
+   unknown_field_set = containers.UnknownFieldSet()
+@@ -977,7 +1027,7 @@ def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
+     if wire_type == wire_format.WIRETYPE_END_GROUP:
+       break
+     (data, pos) = _DecodeUnknownField(
+-        buffer, pos, end_pos, field_number, wire_type
++        buffer, pos, end_pos, field_number, wire_type, current_depth
+     )
+     # pylint: disable=protected-access
+     unknown_field_set._add(field_number, wire_type, data)
+@@ -985,7 +1035,9 @@ def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
+   return (unknown_field_set, pos)
+ 
+ 
+-def _DecodeUnknownField(buffer, pos, end_pos, field_number, wire_type):
++def _DecodeUnknownField(
++    buffer, pos, end_pos, field_number, wire_type, current_depth=0
++):
+   """Decode a unknown field.  Returns the UnknownField and new position."""
+ 
+   if wire_type == wire_format.WIRETYPE_VARINT:
+@@ -1002,7 +1054,7 @@ def _DecodeUnknownField(buffer, pos, end_pos, 
field_number, wire_type):
+     end_tag_bytes = encoder.TagBytes(
+         field_number, wire_format.WIRETYPE_END_GROUP
+     )
+-    data, pos = _DecodeUnknownFieldSet(buffer, pos, end_pos)
++    data, pos = _DecodeUnknownFieldSet(buffer, pos, end_pos, current_depth)
+     # Check end tag.
+     if buffer[pos - len(end_tag_bytes) : pos] != end_tag_bytes:
+       raise _DecodeError('Missing group end tag.')
+diff --git a/python/google/protobuf/internal/message_test.py 
b/python/google/protobuf/internal/message_test.py
+index f8382085b9a00..d9fdf874a7052 100755
+--- a/python/google/protobuf/internal/message_test.py
++++ b/python/google/protobuf/internal/message_test.py
+@@ -60,6 +60,7 @@ from google.protobuf import unittest_pb2
+ from google.protobuf import unittest_proto3_arena_pb2
+ from google.protobuf import descriptor
+ from google.protobuf.internal import api_implementation
++from google.protobuf.internal import decoder
+ from google.protobuf.internal import encoder
+ from google.protobuf.internal import more_extensions_pb2
+ from google.protobuf.internal import packed_field_test_pb2
+diff --git a/python/google/protobuf/internal/python_message.py 
b/python/google/protobuf/internal/python_message.py
+index 23ac28e36d2e0..8cc517c07878d 100755
+--- a/python/google/protobuf/internal/python_message.py
++++ b/python/google/protobuf/internal/python_message.py
+@@ -1141,7 +1141,7 @@ def _AddMergeFromStringMethod(message_de
+   local_SkipField = decoder.SkipField
+   decoders_by_tag = cls._decoders_by_tag
+ 
+-  def InternalParse(self, buffer, pos, end):
++  def InternalParse(self, buffer, pos, end, current_depth=0):
+     """Create a message from serialized bytes.
+ 
+     Args:
+@@ -1193,10 +1193,13 @@ def _AddMergeFromStringMethod(message_de
+             (tag_bytes, buffer[old_pos:new_pos].tobytes()))
+         pos = new_pos
+       else:
+-        pos = field_decoder(buffer, new_pos, end, self, field_dict)
++        pos = field_decoder(
++            buffer, new_pos, end, self, field_dict, current_depth
++        )
+         if field_desc:
+           self._UpdateOneofState(field_desc)
+     return pos
++
+   cls._InternalParse = InternalParse
+ 
+ 
diff -Nru protobuf-3.21.12/debian/patches/CVE-2025-4565-3.patch 
protobuf-3.21.12/debian/patches/CVE-2025-4565-3.patch
--- protobuf-3.21.12/debian/patches/CVE-2025-4565-3.patch       1970-01-01 
02:00:00.000000000 +0200
+++ protobuf-3.21.12/debian/patches/CVE-2025-4565-3.patch       2026-07-02 
22:07:33.000000000 +0300
@@ -0,0 +1,207 @@
+From 17838beda2943d08b8a9d4df5b68f5f04f26d901 Mon Sep 17 00:00:00 2001
+From: Protobuf Team Bot <[email protected]>
+Date: Tue, 13 May 2025 14:42:18 -0700
+Subject: [PATCH] Add recursion depth limits to pure python
+
+PiperOrigin-RevId: 758382549
+
+Origin: upstream, 
https://github.com/protocolbuffers/protobuf/commit/17838beda2943d08b8a9d4df5b68f5f04f26d901.patch
+Bug: https://www.cve.org/CVERecord?id=CVE-2025-4565
+Bug-Debian: https://bugs.debian.org/1108057
+---
+ python/google/protobuf/internal/decoder.py    | 31 ++++++++
+ .../google/protobuf/internal/message_test.py  | 76 ++++++++++++++-----
+ .../protobuf/internal/self_recursive.proto    |  1 +
+ 4 files changed, 102 insertions(+), 19 deletions(-)
+
+diff --git a/python/google/protobuf/internal/decoder.py 
b/python/google/protobuf/internal/decoder.py
+index e2a3de17089ab..dbf848bf31083 100755
+--- a/python/google/protobuf/internal/decoder.py
++++ b/python/google/protobuf/internal/decoder.py
+@@ -693,7 +693,13 @@ def DecodeRepeatedField(
+         if value is None:
+           value = field_dict.setdefault(key, new_default(message))
+         # Read sub-message.
++        current_depth += 1
++        if current_depth > _recursion_limit:
++          raise _DecodeError(
++              'Error parsing message: too many levels of nesting.'
++          )
+         pos = value.add()._InternalParse(buffer, pos, end, current_depth)
++        current_depth -= 1
+         # Read end tag.
+         new_pos = pos+end_tag_len
+         if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
+@@ -712,7 +718,11 @@ def DecodeField(buffer, pos, end, message, field_dict, 
current_depth=0):
+       if value is None:
+         value = field_dict.setdefault(key, new_default(message))
+       # Read sub-message.
++      current_depth += 1
++      if current_depth > _recursion_limit:
++        raise _DecodeError('Error parsing message: too many levels of 
nesting.')
+       pos = value._InternalParse(buffer, pos, end, current_depth)
++      current_depth -= 1
+       # Read end tag.
+       new_pos = pos+end_tag_len
+       if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
+@@ -745,6 +755,11 @@ def DecodeRepeatedField(
+         if new_pos > end:
+           raise _DecodeError('Truncated message.')
+         # Read sub-message.
++        current_depth += 1
++        if current_depth > _recursion_limit:
++          raise _DecodeError(
++              'Error parsing message: too many levels of nesting.'
++          )
+         if (
+             value.add()._InternalParse(buffer, pos, new_pos, current_depth)
+             != new_pos
+@@ -752,6 +767,7 @@ def DecodeRepeatedField(
+           # The only reason _InternalParse would return early is if it
+           # encountered an end-group tag.
+           raise _DecodeError('Unexpected end-group tag.')
++        current_depth -= 1
+         # Predict that the next tag is another copy of the same repeated 
field.
+         pos = new_pos + tag_len
+         if buffer[new_pos:pos] != tag_bytes or new_pos == end:
+@@ -771,10 +787,14 @@ def DecodeField(buffer, pos, end, message, field_dict, 
current_depth=0):
+       if new_pos > end:
+         raise _DecodeError('Truncated message.')
+       # Read sub-message.
++      current_depth += 1
++      if current_depth > _recursion_limit:
++        raise _DecodeError('Error parsing message: too many levels of 
nesting.')
+       if value._InternalParse(buffer, pos, new_pos, current_depth) != new_pos:
+         # The only reason _InternalParse would return early is if it 
encountered
+         # an end-group tag.
+         raise _DecodeError('Unexpected end-group tag.')
++      current_depth -= 1
+       return new_pos
+ 
+     return DecodeField
+@@ -1054,7 +1054,11 @@ def _DecodeUnknownField(
+     end_tag_bytes = encoder.TagBytes(
+         field_number, wire_format.WIRETYPE_END_GROUP
+     )
++    current_depth += 1
++    if current_depth >= _recursion_limit:
++      raise _DecodeError('Error parsing message: too many levels of nesting.')
+     data, pos = _DecodeUnknownFieldSet(buffer, pos, end_pos, current_depth)
++    current_depth -= 1
+     # Check end tag.
+     if buffer[pos - len(end_tag_bytes) : pos] != end_tag_bytes:
+       raise _DecodeError('Missing group end tag.')
+@@ -1086,6 +1086,13 @@ def _DecodeFixed32(buffer, pos):
+ 
+   new_pos = pos + 4
+   return (struct.unpack('<I', buffer[pos:new_pos])[0], new_pos)
++DEFAULT_RECURSION_LIMIT = 100
++_recursion_limit = DEFAULT_RECURSION_LIMIT
++
++
++def SetRecursionLimit(new_limit):
++  global _recursion_limit
++  _recursion_limit = new_limit
+ 
+ 
+ def _RaiseInvalidWireType(buffer, pos, end):
+diff --git a/python/google/protobuf/internal/message_test.py 
b/python/google/protobuf/internal/message_test.py
+index d2c066e6035ec..ea0c707009a14 100755
+--- a/python/google/protobuf/internal/message_test.py
++++ b/python/google/protobuf/internal/message_test.py
+@@ -64,6 +64,7 @@ from google.protobuf.internal import dec
+ from google.protobuf.internal import encoder
+ from google.protobuf.internal import more_extensions_pb2
+ from google.protobuf.internal import packed_field_test_pb2
++from google.protobuf.internal import self_recursive_pb2
+ from google.protobuf.internal import test_util
+ from google.protobuf.internal import test_proto3_optional_pb2
+ from google.protobuf.internal import testing_refleaks
+@@ -1268,6 +1268,52 @@ class MessageTest(unittest.TestCase):
+     self.assertEqual(True, m.repeated_bool[0])
+ 
+ 
++@testing_refleaks.TestCase
++class TestRecursiveGroup(unittest.TestCase):
++
++  def _MakeRecursiveGroupMessage(self, n):
++    msg = self_recursive_pb2.SelfRecursive()
++    sub = msg
++    for _ in range(n):
++      sub = sub.sub_group
++    sub.i = 1
++    return msg.SerializeToString()
++
++  def testRecursiveGroups(self):
++    recurse_msg = self_recursive_pb2.SelfRecursive()
++    data = self._MakeRecursiveGroupMessage(100)
++    recurse_msg.ParseFromString(data)
++    self.assertTrue(recurse_msg.HasField('sub_group'))
++
++  def testRecursiveGroupsException(self):
++    if api_implementation.Type() != 'python':
++      api_implementation._c_module.SetAllowOversizeProtos(False)
++    recurse_msg = self_recursive_pb2.SelfRecursive()
++    data = self._MakeRecursiveGroupMessage(300)
++    with self.assertRaises(message.DecodeError) as context:
++      recurse_msg.ParseFromString(data)
++    self.assertIn('Error parsing message', str(context.exception))
++    if api_implementation.Type() == 'python':
++      self.assertIn('too many levels of nesting', str(context.exception))
++
++  def testRecursiveGroupsUnknownFields(self):
++    if api_implementation.Type() != 'python':
++      api_implementation._c_module.SetAllowOversizeProtos(False)
++    test_msg = unittest_pb2.TestAllTypes()
++    data = self._MakeRecursiveGroupMessage(300)  # unknown to test_msg
++    with self.assertRaises(message.DecodeError) as context:
++      test_msg.ParseFromString(data)
++    self.assertIn(
++        'Error parsing message',
++        str(context.exception),
++    )
++    if api_implementation.Type() == 'python':
++      self.assertIn('too many levels of nesting', str(context.exception))
++      decoder.SetRecursionLimit(310)
++      test_msg.ParseFromString(data)
++      decoder.SetRecursionLimit(decoder.DEFAULT_RECURSION_LIMIT)
++
++
+ # Class to test proto2-only features (required, extensions, etc.)
+ @testing_refleaks.TestCase
+ class Proto2Test(unittest.TestCase):
+@@ -2550,8 +2583,6 @@ def testUnpackedFields(self):
+     self.assertEqual(golden_data, message.SerializeToString())
+ 
+ 
[email protected](api_implementation.Type() == 'python',
+-                 'explicit tests of the C++ implementation')
+ @testing_refleaks.TestCase
+ class OversizeProtosTest(unittest.TestCase):
+ 
+@@ -2568,16 +2599,23 @@ def testSucceedOkSizedProto(self):
+     msg.ParseFromString(self.GenerateNestedProto(100))
+ 
+   def testAssertOversizeProto(self):
+-    api_implementation._c_module.SetAllowOversizeProtos(False)
++    if api_implementation.Type() != 'python':
++      api_implementation._c_module.SetAllowOversizeProtos(False)
+     msg = unittest_pb2.TestRecursiveMessage()
+     with self.assertRaises(message.DecodeError) as context:
+       msg.ParseFromString(self.GenerateNestedProto(101))
+     self.assertIn('Error parsing message', str(context.exception))
+ 
+   def testSucceedOversizeProto(self):
+-    api_implementation._c_module.SetAllowOversizeProtos(True)
++
++    if api_implementation.Type() == 'python':
++      decoder.SetRecursionLimit(310)
++    else:
++      api_implementation._c_module.SetAllowOversizeProtos(True)
++
+     msg = unittest_pb2.TestRecursiveMessage()
+     msg.ParseFromString(self.GenerateNestedProto(101))
++    decoder.SetRecursionLimit(decoder.DEFAULT_RECURSION_LIMIT)
+ 
+ 
+ if __name__ == '__main__':
diff -Nru protobuf-3.21.12/debian/patches/series 
protobuf-3.21.12/debian/patches/series
--- protobuf-3.21.12/debian/patches/series      2025-04-29 22:27:02.000000000 
+0300
+++ protobuf-3.21.12/debian/patches/series      2026-07-02 22:28:01.000000000 
+0300
@@ -19,4 +19,14 @@
 test_scope_for_junit.patch
 stop_using_backward_compatibility_timezones.patch
 python3.12.diff
-CVE-2024-7254.patch
+CVE-2024-7254-1.patch
+CVE-2024-7254-2.patch
+CVE-2024-7254-3.patch
+CVE-2024-7254-4.patch
+CVE-2024-7254-5.patch
+CVE-2025-4565-1.patch
+CVE-2025-4565-2.patch
+CVE-2025-4565-3.patch
+0001-Fix-Any-recursion-depth-bypass-in-Python-json_format.patch
+0002-Check-that-readRaw-does-not-accept-negative-length-v.patch
+0003-php-Fix-that-recursion-limit-is-not-enforced.patch

Reply via email to