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-2025-4565-1.patch | 117
+
patches/CVE-2025-4565-2.patch | 395
+++++
patches/CVE-2025-4565-3.patch | 146
++
patches/series | 11
13 files changed, 2085 insertions(+)
diff -Nru protobuf-3.21.12/debian/changelog protobuf-3.21.12/debian/changelog
--- protobuf-3.21.12/debian/changelog 2023-04-09 08:50:55.000000000 +0300
+++ protobuf-3.21.12/debian/changelog 2026-07-02 22:51:59.000000000 +0300
@@ -1,3 +1,21 @@
+protobuf (3.21.12-3+deb12u1) bookworm; 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:51:59 +0300
+
protobuf (3.21.12-3) unstable; urgency=medium
* Fix build-time tests on 32 bit architectures (closes: #1033998).
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:51:50.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:51:50.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:51:50.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:51:59.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:51:59.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:51:59.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:51:59.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:51:59.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-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:51:59.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:51:59.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:51:59.000000000 +0300
@@ -0,0 +1,146 @@
+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
+@@ -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 2023-04-07 21:12:46.000000000
+0300
+++ protobuf-3.21.12/debian/patches/series 2026-07-02 22:51:59.000000000
+0300
@@ -16,3 +16,14 @@
build_32_bit_tests.patch
fix_C++_32bit_tests.patch
fix_Python_32bit_tests.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