summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta-oe/recipes-devtools/protobuf/protobuf/0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch794
-rw-r--r--meta-oe/recipes-devtools/protobuf/protobuf_4.25.8.bb (renamed from meta-oe/recipes-devtools/protobuf/protobuf_4.25.3.bb)3
2 files changed, 1 insertions, 796 deletions
diff --git a/meta-oe/recipes-devtools/protobuf/protobuf/0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch b/meta-oe/recipes-devtools/protobuf/protobuf/0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch
deleted file mode 100644
index 2f14620b4a..0000000000
--- a/meta-oe/recipes-devtools/protobuf/protobuf/0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch
+++ /dev/null
@@ -1,794 +0,0 @@
1From 9f182ae260cc60e8cc5417abbe9481821642afa0 Mon Sep 17 00:00:00 2001
2From: Protobuf Team Bot <protobuf-github-bot@google.com>
3Date: Tue, 17 Sep 2024 12:03:36 -0700
4Subject: [PATCH] Add recursion check when parsing unknown fields in Java.
5
6PiperOrigin-RevId: 675657198
7
8CVE: CVE-2024-7254
9
10Upstream-Status: Backport [ac9fb5b4c71b0dd80985b27684e265d1f03abf46]
11
12The original patch is adjusted to fit for the current version.
13
14Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
15---
16 .../com/google/protobuf/ArrayDecoders.java | 28 +++
17 .../com/google/protobuf/CodedInputStream.java | 72 +++++-
18 .../com/google/protobuf/MessageSchema.java | 9 +-
19 .../com/google/protobuf/MessageSetSchema.java | 2 +-
20 .../google/protobuf/UnknownFieldSchema.java | 28 ++-
21 .../google/protobuf/CodedInputStreamTest.java | 159 ++++++++++++
22 .../java/com/google/protobuf/LiteTest.java | 232 ++++++++++++++++++
23 7 files changed, 514 insertions(+), 16 deletions(-)
24
25diff --git a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
26index f3241de50..0f3d7de0d 100644
27--- a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
28+++ b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java
29@@ -26,6 +26,10 @@ final class ArrayDecoders {
30
31 private ArrayDecoders() {
32 }
33+ static final int DEFAULT_RECURSION_LIMIT = 100;
34+
35+ @SuppressWarnings("NonFinalStaticField")
36+ private static volatile int recursionLimit = DEFAULT_RECURSION_LIMIT;
37
38 /**
39 * A helper used to return multiple values in a Java function. Java doesn't natively support
40@@ -38,6 +42,7 @@ final class ArrayDecoders {
41 public long long1;
42 public Object object1;
43 public final ExtensionRegistryLite extensionRegistry;
44+ public int recursionDepth;
45
46 Registers() {
47 this.extensionRegistry = ExtensionRegistryLite.getEmptyRegistry();
48@@ -245,7 +250,10 @@ final class ArrayDecoders {
49 if (length < 0 || length > limit - position) {
50 throw InvalidProtocolBufferException.truncatedMessage();
51 }
52+ registers.recursionDepth++;
53+ checkRecursionLimit(registers.recursionDepth);
54 schema.mergeFrom(msg, data, position, position + length, registers);
55+ registers.recursionDepth--;
56 registers.object1 = msg;
57 return position + length;
58 }
59@@ -263,8 +271,11 @@ final class ArrayDecoders {
60 // A group field must has a MessageSchema (the only other subclass of Schema is MessageSetSchema
61 // and it can't be used in group fields).
62 final MessageSchema messageSchema = (MessageSchema) schema;
63+ registers.recursionDepth++;
64+ checkRecursionLimit(registers.recursionDepth);
65 final int endPosition =
66 messageSchema.parseMessage(msg, data, position, limit, endGroup, registers);
67+ registers.recursionDepth--;
68 registers.object1 = msg;
69 return endPosition;
70 }
71@@ -1025,6 +1036,8 @@ final class ArrayDecoders {
72 final UnknownFieldSetLite child = UnknownFieldSetLite.newInstance();
73 final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP;
74 int lastTag = 0;
75+ registers.recursionDepth++;
76+ checkRecursionLimit(registers.recursionDepth);
77 while (position < limit) {
78 position = decodeVarint32(data, position, registers);
79 lastTag = registers.int1;
80@@ -1033,6 +1046,7 @@ final class ArrayDecoders {
81 }
82 position = decodeUnknownField(lastTag, data, position, limit, child, registers);
83 }
84+ registers.recursionDepth--;
85 if (position > limit || lastTag != endGroup) {
86 throw InvalidProtocolBufferException.parseFailure();
87 }
88@@ -1079,4 +1093,18 @@ final class ArrayDecoders {
89 throw InvalidProtocolBufferException.invalidTag();
90 }
91 }
92+
93+ /**
94+ * Set the maximum recursion limit that ArrayDecoders will allow. An exception will be thrown if
95+ * the depth of the message exceeds this limit.
96+ */
97+ public static void setRecursionLimit(int limit) {
98+ recursionLimit = limit;
99+ }
100+
101+ private static void checkRecursionLimit(int depth) throws InvalidProtocolBufferException {
102+ if (depth >= recursionLimit) {
103+ throw InvalidProtocolBufferException.recursionLimitExceeded();
104+ }
105+ }
106 }
107diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
108index 8f1ac736d..29256b4b3 100644
109--- a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
110+++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
111@@ -703,7 +703,14 @@ public abstract class CodedInputStream {
112 public void skipMessage() throws IOException {
113 while (true) {
114 final int tag = readTag();
115- if (tag == 0 || !skipField(tag)) {
116+ if (tag == 0) {
117+ return;
118+ }
119+ checkRecursionLimit();
120+ ++recursionDepth;
121+ boolean fieldSkipped = skipField(tag);
122+ --recursionDepth;
123+ if (!fieldSkipped) {
124 return;
125 }
126 }
127@@ -713,7 +720,14 @@ public abstract class CodedInputStream {
128 public void skipMessage(CodedOutputStream output) throws IOException {
129 while (true) {
130 final int tag = readTag();
131- if (tag == 0 || !skipField(tag, output)) {
132+ if (tag == 0) {
133+ return;
134+ }
135+ checkRecursionLimit();
136+ ++recursionDepth;
137+ boolean fieldSkipped = skipField(tag, output);
138+ --recursionDepth;
139+ if (!fieldSkipped) {
140 return;
141 }
142 }
143@@ -1415,7 +1429,14 @@ public abstract class CodedInputStream {
144 public void skipMessage() throws IOException {
145 while (true) {
146 final int tag = readTag();
147- if (tag == 0 || !skipField(tag)) {
148+ if (tag == 0) {
149+ return;
150+ }
151+ checkRecursionLimit();
152+ ++recursionDepth;
153+ boolean fieldSkipped = skipField(tag);
154+ --recursionDepth;
155+ if (!fieldSkipped) {
156 return;
157 }
158 }
159@@ -1425,7 +1446,14 @@ public abstract class CodedInputStream {
160 public void skipMessage(CodedOutputStream output) throws IOException {
161 while (true) {
162 final int tag = readTag();
163- if (tag == 0 || !skipField(tag, output)) {
164+ if (tag == 0) {
165+ return;
166+ }
167+ checkRecursionLimit();
168+ ++recursionDepth;
169+ boolean fieldSkipped = skipField(tag, output);
170+ --recursionDepth;
171+ if (!fieldSkipped) {
172 return;
173 }
174 }
175@@ -2180,7 +2208,14 @@ public abstract class CodedInputStream {
176 public void skipMessage() throws IOException {
177 while (true) {
178 final int tag = readTag();
179- if (tag == 0 || !skipField(tag)) {
180+ if (tag == 0) {
181+ return;
182+ }
183+ checkRecursionLimit();
184+ ++recursionDepth;
185+ boolean fieldSkipped = skipField(tag);
186+ --recursionDepth;
187+ if (!fieldSkipped) {
188 return;
189 }
190 }
191@@ -2190,7 +2225,14 @@ public abstract class CodedInputStream {
192 public void skipMessage(CodedOutputStream output) throws IOException {
193 while (true) {
194 final int tag = readTag();
195- if (tag == 0 || !skipField(tag, output)) {
196+ if (tag == 0) {
197+ return;
198+ }
199+ checkRecursionLimit();
200+ ++recursionDepth;
201+ boolean fieldSkipped = skipField(tag, output);
202+ --recursionDepth;
203+ if (!fieldSkipped) {
204 return;
205 }
206 }
207@@ -3298,7 +3340,14 @@ public abstract class CodedInputStream {
208 public void skipMessage() throws IOException {
209 while (true) {
210 final int tag = readTag();
211- if (tag == 0 || !skipField(tag)) {
212+ if (tag == 0) {
213+ return;
214+ }
215+ checkRecursionLimit();
216+ ++recursionDepth;
217+ boolean fieldSkipped = skipField(tag);
218+ --recursionDepth;
219+ if (!fieldSkipped) {
220 return;
221 }
222 }
223@@ -3308,7 +3357,14 @@ public abstract class CodedInputStream {
224 public void skipMessage(CodedOutputStream output) throws IOException {
225 while (true) {
226 final int tag = readTag();
227- if (tag == 0 || !skipField(tag, output)) {
228+ if (tag == 0) {
229+ return;
230+ }
231+ checkRecursionLimit();
232+ ++recursionDepth;
233+ boolean fieldSkipped = skipField(tag, output);
234+ --recursionDepth;
235+ if (!fieldSkipped) {
236 return;
237 }
238 }
239diff --git a/java/core/src/main/java/com/google/protobuf/MessageSchema.java b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
240index de3890f70..5ad6762b0 100644
241--- a/java/core/src/main/java/com/google/protobuf/MessageSchema.java
242+++ b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
243@@ -3006,7 +3006,8 @@ final class MessageSchema<T> implements Schema<T> {
244 unknownFields = unknownFieldSchema.getBuilderFromMessage(message);
245 }
246 // Unknown field.
247- if (unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
248+ if (unknownFieldSchema.mergeOneFieldFrom(
249+ unknownFields, reader, /* currentDepth= */ 0)) {
250 continue;
251 }
252 }
253@@ -3381,7 +3382,8 @@ final class MessageSchema<T> implements Schema<T> {
254 if (unknownFields == null) {
255 unknownFields = unknownFieldSchema.getBuilderFromMessage(message);
256 }
257- if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
258+ if (!unknownFieldSchema.mergeOneFieldFrom(
259+ unknownFields, reader, /* currentDepth= */ 0)) {
260 return;
261 }
262 break;
263@@ -3397,7 +3399,8 @@ final class MessageSchema<T> implements Schema<T> {
264 if (unknownFields == null) {
265 unknownFields = unknownFieldSchema.getBuilderFromMessage(message);
266 }
267- if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) {
268+ if (!unknownFieldSchema.mergeOneFieldFrom(
269+ unknownFields, reader, /* currentDepth= */ 0)) {
270 return;
271 }
272 }
273diff --git a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
274index eec3acd35..ec37d41f9 100644
275--- a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
276+++ b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java
277@@ -278,7 +278,7 @@ final class MessageSetSchema<T> implements Schema<T> {
278 reader, extension, extensionRegistry, extensions);
279 return true;
280 } else {
281- return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader);
282+ return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader, /* currentDepth= */ 0);
283 }
284 } else {
285 return reader.skipField();
286diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
287index c4ec645bf..0cdecd30e 100644
288--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
289+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java
290@@ -13,6 +13,11 @@ import java.io.IOException;
291 @CheckReturnValue
292 abstract class UnknownFieldSchema<T, B> {
293
294+ static final int DEFAULT_RECURSION_LIMIT = 100;
295+
296+ @SuppressWarnings("NonFinalStaticField")
297+ private static volatile int recursionLimit = DEFAULT_RECURSION_LIMIT;
298+
299 /** Whether unknown fields should be dropped. */
300 abstract boolean shouldDiscardUnknownFields(Reader reader);
301
302@@ -56,7 +61,8 @@ abstract class UnknownFieldSchema<T, B> {
303 abstract void makeImmutable(Object message);
304
305 /** Merges one field into the unknown fields. */
306- final boolean mergeOneFieldFrom(B unknownFields, Reader reader) throws IOException {
307+ final boolean mergeOneFieldFrom(B unknownFields, Reader reader, int currentDepth)
308+ throws IOException {
309 int tag = reader.getTag();
310 int fieldNumber = WireFormat.getTagFieldNumber(tag);
311 switch (WireFormat.getTagWireType(tag)) {
312@@ -75,7 +81,12 @@ abstract class UnknownFieldSchema<T, B> {
313 case WireFormat.WIRETYPE_START_GROUP:
314 final B subFields = newBuilder();
315 int endGroupTag = WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
316- mergeFrom(subFields, reader);
317+ currentDepth++;
318+ if (currentDepth >= recursionLimit) {
319+ throw InvalidProtocolBufferException.recursionLimitExceeded();
320+ }
321+ mergeFrom(subFields, reader, currentDepth);
322+ currentDepth--;
323 if (endGroupTag != reader.getTag()) {
324 throw InvalidProtocolBufferException.invalidEndTag();
325 }
326@@ -88,10 +99,11 @@ abstract class UnknownFieldSchema<T, B> {
327 }
328 }
329
330- final void mergeFrom(B unknownFields, Reader reader) throws IOException {
331+ final void mergeFrom(B unknownFields, Reader reader, int currentDepth)
332+ throws IOException {
333 while (true) {
334 if (reader.getFieldNumber() == Reader.READ_DONE
335- || !mergeOneFieldFrom(unknownFields, reader)) {
336+ || !mergeOneFieldFrom(unknownFields, reader, currentDepth)) {
337 break;
338 }
339 }
340@@ -108,4 +120,12 @@ abstract class UnknownFieldSchema<T, B> {
341 abstract int getSerializedSizeAsMessageSet(T message);
342
343 abstract int getSerializedSize(T unknowns);
344+
345+ /**
346+ * Set the maximum recursion limit that ArrayDecoders will allow. An exception will be thrown if
347+ * the depth of the message exceeds this limit.
348+ */
349+ public void setRecursionLimit(int limit) {
350+ recursionLimit = limit;
351+ }
352 }
353diff --git a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
354index 2de3273e3..19a6b669d 100644
355--- a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
356+++ b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
357@@ -10,6 +10,10 @@ package com.google.protobuf;
358 import static com.google.common.truth.Truth.assertThat;
359 import static com.google.common.truth.Truth.assertWithMessage;
360 import static org.junit.Assert.assertArrayEquals;
361+import static org.junit.Assert.assertThrows;
362+
363+import com.google.common.primitives.Bytes;
364+import map_test.MapTestProto.MapContainer;
365 import protobuf_unittest.UnittestProto.BoolMessage;
366 import protobuf_unittest.UnittestProto.Int32Message;
367 import protobuf_unittest.UnittestProto.Int64Message;
368@@ -34,6 +38,13 @@ public class CodedInputStreamTest {
369
370 private static final int DEFAULT_BLOCK_SIZE = 4096;
371
372+ private static final int GROUP_TAP = WireFormat.makeTag(3, WireFormat.WIRETYPE_START_GROUP);
373+
374+ private static final byte[] NESTING_SGROUP = generateSGroupTags();
375+
376+ private static final byte[] NESTING_SGROUP_WITH_INITIAL_BYTES = generateSGroupTagsForMapField();
377+
378+
379 private enum InputType {
380 ARRAY {
381 @Override
382@@ -116,6 +127,17 @@ public class CodedInputStreamTest {
383 return bytes;
384 }
385
386+ private static byte[] generateSGroupTags() {
387+ byte[] bytes = new byte[100000];
388+ Arrays.fill(bytes, (byte) GROUP_TAP);
389+ return bytes;
390+ }
391+
392+ private static byte[] generateSGroupTagsForMapField() {
393+ byte[] initialBytes = {18, 1, 75, 26, (byte) 198, (byte) 154, 12};
394+ return Bytes.concat(initialBytes, NESTING_SGROUP);
395+ }
396+
397 /**
398 * An InputStream which limits the number of bytes it reads at a time. We use this to make sure
399 * that CodedInputStream doesn't screw up when reading in small blocks.
400@@ -659,6 +681,143 @@ public class CodedInputStreamTest {
401 }
402 }
403
404+ @Test
405+ public void testMaliciousRecursion_unknownFields() throws Exception {
406+ Throwable thrown =
407+ assertThrows(
408+ InvalidProtocolBufferException.class,
409+ () -> TestRecursiveMessage.parseFrom(NESTING_SGROUP));
410+
411+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
412+ }
413+
414+ @Test
415+ public void testMaliciousRecursion_skippingUnknownField() throws Exception {
416+ Throwable thrown =
417+ assertThrows(
418+ InvalidProtocolBufferException.class,
419+ () ->
420+ DiscardUnknownFieldsParser.wrap(TestRecursiveMessage.parser())
421+ .parseFrom(NESTING_SGROUP));
422+
423+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
424+ }
425+
426+ @Test
427+ public void testMaliciousSGroupTagsWithMapField_fromInputStream() throws Exception {
428+ Throwable parseFromThrown =
429+ assertThrows(
430+ InvalidProtocolBufferException.class,
431+ () ->
432+ MapContainer.parseFrom(
433+ new ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
434+ Throwable mergeFromThrown =
435+ assertThrows(
436+ InvalidProtocolBufferException.class,
437+ () ->
438+ MapContainer.newBuilder()
439+ .mergeFrom(new ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
440+
441+ assertThat(parseFromThrown)
442+ .hasMessageThat()
443+ .contains("Protocol message had too many levels of nesting");
444+ assertThat(mergeFromThrown)
445+ .hasMessageThat()
446+ .contains("Protocol message had too many levels of nesting");
447+ }
448+
449+ @Test
450+ public void testMaliciousSGroupTags_inputStream_skipMessage() throws Exception {
451+ ByteArrayInputStream inputSteam = new ByteArrayInputStream(NESTING_SGROUP);
452+ CodedInputStream input = CodedInputStream.newInstance(inputSteam);
453+ CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
454+
455+ Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
456+ Throwable thrown2 =
457+ assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
458+
459+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
460+ assertThat(thrown2)
461+ .hasMessageThat()
462+ .contains("Protocol message had too many levels of nesting");
463+ }
464+
465+ @Test
466+ public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws Exception {
467+ Throwable parseFromThrown =
468+ assertThrows(
469+ InvalidProtocolBufferException.class,
470+ () -> MapContainer.parseFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
471+ Throwable mergeFromThrown =
472+ assertThrows(
473+ InvalidProtocolBufferException.class,
474+ () -> MapContainer.newBuilder().mergeFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
475+
476+ assertThat(parseFromThrown)
477+ .hasMessageThat()
478+ .contains("the input ended unexpectedly in the middle of a field");
479+ assertThat(mergeFromThrown)
480+ .hasMessageThat()
481+ .contains("the input ended unexpectedly in the middle of a field");
482+ }
483+
484+ @Test
485+ public void testMaliciousSGroupTags_arrayDecoder_skipMessage() throws Exception {
486+ CodedInputStream input = CodedInputStream.newInstance(NESTING_SGROUP);
487+ CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
488+
489+ Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
490+ Throwable thrown2 =
491+ assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
492+
493+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
494+ assertThat(thrown2)
495+ .hasMessageThat()
496+ .contains("Protocol message had too many levels of nesting");
497+ }
498+
499+ @Test
500+ public void testMaliciousSGroupTagsWithMapField_fromByteBuffer() throws Exception {
501+ Throwable thrown =
502+ assertThrows(
503+ InvalidProtocolBufferException.class,
504+ () -> MapContainer.parseFrom(ByteBuffer.wrap(NESTING_SGROUP_WITH_INITIAL_BYTES)));
505+
506+ assertThat(thrown)
507+ .hasMessageThat()
508+ .contains("the input ended unexpectedly in the middle of a field");
509+ }
510+
511+ @Test
512+ public void testMaliciousSGroupTags_byteBuffer_skipMessage() throws Exception {
513+ CodedInputStream input = InputType.NIO_DIRECT.newDecoder(NESTING_SGROUP);
514+ CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
515+
516+ Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
517+ Throwable thrown2 =
518+ assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
519+
520+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
521+ assertThat(thrown2)
522+ .hasMessageThat()
523+ .contains("Protocol message had too many levels of nesting");
524+ }
525+
526+ @Test
527+ public void testMaliciousSGroupTags_iterableByteBuffer() throws Exception {
528+ CodedInputStream input = InputType.ITER_DIRECT.newDecoder(NESTING_SGROUP);
529+ CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
530+
531+ Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
532+ Throwable thrown2 =
533+ assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
534+
535+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
536+ assertThat(thrown2)
537+ .hasMessageThat()
538+ .contains("Protocol message had too many levels of nesting");
539+ }
540+
541 private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
542 assertThat(e)
543 .hasMessageThat()
544diff --git a/java/lite/src/test/java/com/google/protobuf/LiteTest.java b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
545index 754ed7d5f..81be90bfd 100644
546--- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java
547+++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
548@@ -2459,6 +2459,211 @@ public class LiteTest {
549 }
550 }
551
552+ @Test
553+ public void testParseFromInputStream_concurrent_nestingUnknownGroups() throws Exception {
554+ int numThreads = 200;
555+ ArrayList<Thread> threads = new ArrayList<>();
556+
557+ ByteString byteString = generateNestingGroups(99);
558+ AtomicBoolean thrown = new AtomicBoolean(false);
559+
560+ for (int i = 0; i < numThreads; i++) {
561+ Thread thread =
562+ new Thread(
563+ () -> {
564+ try {
565+ TestAllTypesLite unused = TestAllTypesLite.parseFrom(byteString);
566+ } catch (IOException e) {
567+ if (e.getMessage().contains("Protocol message had too many levels of nesting")) {
568+ thrown.set(true);
569+ }
570+ }
571+ });
572+ thread.start();
573+ threads.add(thread);
574+ }
575+
576+ for (Thread thread : threads) {
577+ thread.join();
578+ }
579+
580+ assertThat(thrown.get()).isFalse();
581+ }
582+
583+ @Test
584+ public void testParseFromInputStream_nestingUnknownGroups() throws IOException {
585+ ByteString byteString = generateNestingGroups(99);
586+
587+ Throwable thrown =
588+ assertThrows(
589+ InvalidProtocolBufferException.class, () -> TestAllTypesLite.parseFrom(byteString));
590+ assertThat(thrown)
591+ .hasMessageThat()
592+ .doesNotContain("Protocol message had too many levels of nesting");
593+ }
594+
595+ @Test
596+ public void testParseFromInputStream_nestingUnknownGroups_exception() throws IOException {
597+ ByteString byteString = generateNestingGroups(100);
598+
599+ Throwable thrown =
600+ assertThrows(
601+ InvalidProtocolBufferException.class, () -> TestAllTypesLite.parseFrom(byteString));
602+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
603+ }
604+
605+ @Test
606+ public void testParseFromInputStream_setRecursionLimit_exception() throws IOException {
607+ ByteString byteString = generateNestingGroups(199);
608+ UnknownFieldSchema<?, ?> schema = SchemaUtil.unknownFieldSetLiteSchema();
609+ schema.setRecursionLimit(200);
610+
611+ Throwable thrown =
612+ assertThrows(
613+ InvalidProtocolBufferException.class, () -> TestAllTypesLite.parseFrom(byteString));
614+ assertThat(thrown)
615+ .hasMessageThat()
616+ .doesNotContain("Protocol message had too many levels of nesting");
617+ schema.setRecursionLimit(UnknownFieldSchema.DEFAULT_RECURSION_LIMIT);
618+ }
619+
620+ @Test
621+ public void testParseFromBytes_concurrent_nestingUnknownGroups() throws Exception {
622+ int numThreads = 200;
623+ ArrayList<Thread> threads = new ArrayList<>();
624+
625+ ByteString byteString = generateNestingGroups(99);
626+ AtomicBoolean thrown = new AtomicBoolean(false);
627+
628+ for (int i = 0; i < numThreads; i++) {
629+ Thread thread =
630+ new Thread(
631+ () -> {
632+ try {
633+ // Should pass in byte[] instead of ByteString to go into ArrayDecoders.
634+ TestAllTypesLite unused = TestAllTypesLite.parseFrom(byteString.toByteArray());
635+ } catch (InvalidProtocolBufferException e) {
636+ if (e.getMessage().contains("Protocol message had too many levels of nesting")) {
637+ thrown.set(true);
638+ }
639+ }
640+ });
641+ thread.start();
642+ threads.add(thread);
643+ }
644+
645+ for (Thread thread : threads) {
646+ thread.join();
647+ }
648+
649+ assertThat(thrown.get()).isFalse();
650+ }
651+
652+ @Test
653+ public void testParseFromBytes_nestingUnknownGroups() throws IOException {
654+ ByteString byteString = generateNestingGroups(99);
655+
656+ Throwable thrown =
657+ assertThrows(
658+ InvalidProtocolBufferException.class,
659+ () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
660+ assertThat(thrown)
661+ .hasMessageThat()
662+ .doesNotContain("Protocol message had too many levels of nesting");
663+ }
664+
665+ @Test
666+ public void testParseFromBytes_nestingUnknownGroups_exception() throws IOException {
667+ ByteString byteString = generateNestingGroups(100);
668+
669+ Throwable thrown =
670+ assertThrows(
671+ InvalidProtocolBufferException.class,
672+ () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
673+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
674+ }
675+
676+ @Test
677+ public void testParseFromBytes_setRecursionLimit_exception() throws IOException {
678+ ByteString byteString = generateNestingGroups(199);
679+ ArrayDecoders.setRecursionLimit(200);
680+
681+ Throwable thrown =
682+ assertThrows(
683+ InvalidProtocolBufferException.class,
684+ () -> TestAllTypesLite.parseFrom(byteString.toByteArray()));
685+ assertThat(thrown)
686+ .hasMessageThat()
687+ .doesNotContain("Protocol message had too many levels of nesting");
688+ ArrayDecoders.setRecursionLimit(ArrayDecoders.DEFAULT_RECURSION_LIMIT);
689+ }
690+
691+ @Test
692+ public void testParseFromBytes_recursiveMessages() throws Exception {
693+ byte[] data99 = makeRecursiveMessage(99).toByteArray();
694+ byte[] data100 = makeRecursiveMessage(100).toByteArray();
695+
696+ RecursiveMessage unused = RecursiveMessage.parseFrom(data99);
697+ Throwable thrown =
698+ assertThrows(
699+ InvalidProtocolBufferException.class, () -> RecursiveMessage.parseFrom(data100));
700+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
701+ }
702+
703+ @Test
704+ public void testParseFromBytes_recursiveKnownGroups() throws Exception {
705+ byte[] data99 = makeRecursiveGroup(99).toByteArray();
706+ byte[] data100 = makeRecursiveGroup(100).toByteArray();
707+
708+ RecursiveGroup unused = RecursiveGroup.parseFrom(data99);
709+ Throwable thrown =
710+ assertThrows(InvalidProtocolBufferException.class, () -> RecursiveGroup.parseFrom(data100));
711+ assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
712+ }
713+
714+ @Test
715+ @SuppressWarnings("ProtoParseFromByteString")
716+ public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws Exception {
717+ ByteString byteString = generateNestingGroups(102);
718+
719+ Throwable parseFromThrown =
720+ assertThrows(
721+ InvalidProtocolBufferException.class,
722+ () -> MapContainer.parseFrom(byteString.toByteArray()));
723+ Throwable mergeFromThrown =
724+ assertThrows(
725+ InvalidProtocolBufferException.class,
726+ () -> MapContainer.newBuilder().mergeFrom(byteString.toByteArray()));
727+
728+ assertThat(parseFromThrown)
729+ .hasMessageThat()
730+ .contains("Protocol message had too many levels of nesting");
731+ assertThat(mergeFromThrown)
732+ .hasMessageThat()
733+ .contains("Protocol message had too many levels of nesting");
734+ }
735+
736+ @Test
737+ public void testMaliciousSGroupTagsWithMapField_fromInputStream() throws Exception {
738+ byte[] bytes = generateNestingGroups(101).toByteArray();
739+
740+ Throwable parseFromThrown =
741+ assertThrows(
742+ InvalidProtocolBufferException.class,
743+ () -> MapContainer.parseFrom(new ByteArrayInputStream(bytes)));
744+ Throwable mergeFromThrown =
745+ assertThrows(
746+ InvalidProtocolBufferException.class,
747+ () -> MapContainer.newBuilder().mergeFrom(new ByteArrayInputStream(bytes)));
748+
749+ assertThat(parseFromThrown)
750+ .hasMessageThat()
751+ .contains("Protocol message had too many levels of nesting");
752+ assertThat(mergeFromThrown)
753+ .hasMessageThat()
754+ .contains("Protocol message had too many levels of nesting");
755+ }
756+
757 @Test
758 public void testParseFromByteBuffer_extensions() throws Exception {
759 TestAllExtensionsLite message =
760@@ -2815,4 +3020,31 @@ public class LiteTest {
761 }
762 return false;
763 }
764+
765+ private static ByteString generateNestingGroups(int num) throws IOException {
766+ int groupTap = WireFormat.makeTag(3, WireFormat.WIRETYPE_START_GROUP);
767+ ByteString.Output byteStringOutput = ByteString.newOutput();
768+ CodedOutputStream codedOutput = CodedOutputStream.newInstance(byteStringOutput);
769+ for (int i = 0; i < num; i++) {
770+ codedOutput.writeInt32NoTag(groupTap);
771+ }
772+ codedOutput.flush();
773+ return byteStringOutput.toByteString();
774+ }
775+
776+ private static RecursiveMessage makeRecursiveMessage(int num) {
777+ if (num == 0) {
778+ return RecursiveMessage.getDefaultInstance();
779+ } else {
780+ return RecursiveMessage.newBuilder().setRecurse(makeRecursiveMessage(num - 1)).build();
781+ }
782+ }
783+
784+ private static RecursiveGroup makeRecursiveGroup(int num) {
785+ if (num == 0) {
786+ return RecursiveGroup.getDefaultInstance();
787+ } else {
788+ return RecursiveGroup.newBuilder().setRecurse(makeRecursiveGroup(num - 1)).build();
789+ }
790+ }
791 }
792--
7932.25.1
794
diff --git a/meta-oe/recipes-devtools/protobuf/protobuf_4.25.3.bb b/meta-oe/recipes-devtools/protobuf/protobuf_4.25.8.bb
index acc35db4a5..949a3b207b 100644
--- a/meta-oe/recipes-devtools/protobuf/protobuf_4.25.3.bb
+++ b/meta-oe/recipes-devtools/protobuf/protobuf_4.25.8.bb
@@ -10,12 +10,11 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b"
10DEPENDS = "zlib abseil-cpp" 10DEPENDS = "zlib abseil-cpp"
11DEPENDS:append:class-target = " protobuf-native" 11DEPENDS:append:class-target = " protobuf-native"
12 12
13SRCREV = "4a2aef570deb2bfb8927426558701e8bfc26f2a4" 13SRCREV = "a4cbdd3ed0042e8f9b9c30e8b0634096d9532809"
14 14
15SRC_URI = "gitsm://github.com/protocolbuffers/protobuf.git;branch=25.x;protocol=https \ 15SRC_URI = "gitsm://github.com/protocolbuffers/protobuf.git;branch=25.x;protocol=https \
16 file://run-ptest \ 16 file://run-ptest \
17 file://0001-examples-Makefile-respect-CXX-LDFLAGS-variables-fix-.patch \ 17 file://0001-examples-Makefile-respect-CXX-LDFLAGS-variables-fix-.patch \
18 file://0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch \
19 " 18 "
20SRC_URI:append:mips:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch " 19SRC_URI:append:mips:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch "
21SRC_URI:append:mipsel:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch " 20SRC_URI:append:mipsel:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch "