• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.common.truth.Truth.assertThat;
11 import static com.google.common.truth.Truth.assertWithMessage;
12 import static org.junit.Assert.assertArrayEquals;
13 import static org.junit.Assert.assertThrows;
14 
15 import com.google.common.primitives.Bytes;
16 import map_test.MapTestProto.MapContainer;
17 import protobuf_unittest.UnittestProto.BoolMessage;
18 import protobuf_unittest.UnittestProto.Int32Message;
19 import protobuf_unittest.UnittestProto.Int64Message;
20 import protobuf_unittest.UnittestProto.TestAllTypes;
21 import protobuf_unittest.UnittestProto.TestRecursiveMessage;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.FilterInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.nio.ByteBuffer;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.JUnit4;
34 
35 /** Unit test for {@link CodedInputStream}. */
36 @RunWith(JUnit4.class)
37 public class CodedInputStreamTest {
38 
39   private static final int DEFAULT_BLOCK_SIZE = 4096;
40 
41   private static final int GROUP_TAP = WireFormat.makeTag(3, WireFormat.WIRETYPE_START_GROUP);
42 
43   private static final byte[] NESTING_SGROUP = generateSGroupTags();
44 
45   private static final byte[] NESTING_SGROUP_WITH_INITIAL_BYTES = generateSGroupTagsForMapField();
46 
47 
48   private enum InputType {
49     ARRAY {
50       @Override
newDecoder(byte[] data, int blockSize)51       CodedInputStream newDecoder(byte[] data, int blockSize) {
52         return CodedInputStream.newInstance(data);
53       }
54     },
55     NIO_HEAP {
56       @Override
newDecoder(byte[] data, int blockSize)57       CodedInputStream newDecoder(byte[] data, int blockSize) {
58         return CodedInputStream.newInstance(ByteBuffer.wrap(data));
59       }
60     },
61     NIO_DIRECT {
62       @Override
newDecoder(byte[] data, int blockSize)63       CodedInputStream newDecoder(byte[] data, int blockSize) {
64         ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
65         buffer.put(data);
66         buffer.flip();
67         return CodedInputStream.newInstance(buffer);
68       }
69     },
70     STREAM {
71       @Override
newDecoder(byte[] data, int blockSize)72       CodedInputStream newDecoder(byte[] data, int blockSize) {
73         return CodedInputStream.newInstance(new SmallBlockInputStream(data, blockSize));
74       }
75     },
76     ITER_DIRECT {
77       @Override
newDecoder(byte[] data, int blockSize)78       CodedInputStream newDecoder(byte[] data, int blockSize) {
79         if (blockSize > DEFAULT_BLOCK_SIZE) {
80           blockSize = DEFAULT_BLOCK_SIZE;
81         }
82         ArrayList<ByteBuffer> input = new ArrayList<ByteBuffer>();
83         for (int i = 0; i < data.length; i += blockSize) {
84           int rl = Math.min(blockSize, data.length - i);
85           ByteBuffer rb = ByteBuffer.allocateDirect(rl);
86           rb.put(data, i, rl);
87           rb.flip();
88           input.add(rb);
89         }
90         return CodedInputStream.newInstance(input);
91       }
92     },
93     STREAM_ITER_DIRECT {
94       @Override
newDecoder(byte[] data, int blockSize)95       CodedInputStream newDecoder(byte[] data, int blockSize) {
96         if (blockSize > DEFAULT_BLOCK_SIZE) {
97           blockSize = DEFAULT_BLOCK_SIZE;
98         }
99         ArrayList<ByteBuffer> input = new ArrayList<ByteBuffer>();
100         for (int i = 0; i < data.length; i += blockSize) {
101           int rl = Math.min(blockSize, data.length - i);
102           ByteBuffer rb = ByteBuffer.allocateDirect(rl);
103           rb.put(data, i, rl);
104           rb.flip();
105           input.add(rb);
106         }
107         return CodedInputStream.newInstance(new IterableByteBufferInputStream(input));
108       }
109     };
110 
newDecoder(byte[] data)111     CodedInputStream newDecoder(byte[] data) {
112       return newDecoder(data, data.length);
113     }
114 
newDecoder(byte[] data, int blockSize)115     abstract CodedInputStream newDecoder(byte[] data, int blockSize);
116   }
117 
118   /**
119    * Helper to construct a byte array from a bunch of bytes. The inputs are actually ints so that I
120    * can use hex notation and not get stupid errors about precision.
121    */
bytes(int... bytesAsInts)122   private byte[] bytes(int... bytesAsInts) {
123     byte[] bytes = new byte[bytesAsInts.length];
124     for (int i = 0; i < bytesAsInts.length; i++) {
125       bytes[i] = (byte) bytesAsInts[i];
126     }
127     return bytes;
128   }
129 
generateSGroupTags()130   private static byte[] generateSGroupTags() {
131     byte[] bytes = new byte[100000];
132     Arrays.fill(bytes, (byte) GROUP_TAP);
133     return bytes;
134   }
135 
generateSGroupTagsForMapField()136   private static byte[] generateSGroupTagsForMapField() {
137     byte[] initialBytes = {18, 1, 75, 26, (byte) 198, (byte) 154, 12};
138     return Bytes.concat(initialBytes, NESTING_SGROUP);
139   }
140 
141   /**
142    * An InputStream which limits the number of bytes it reads at a time. We use this to make sure
143    * that CodedInputStream doesn't screw up when reading in small blocks.
144    */
145   private static final class SmallBlockInputStream extends FilterInputStream {
146     private final int blockSize;
147     private int skipCalls;
148     private int readCalls;
149 
SmallBlockInputStream(byte[] data, int blockSize)150     public SmallBlockInputStream(byte[] data, int blockSize) {
151       super(new ByteArrayInputStream(data));
152       this.blockSize = blockSize;
153     }
154 
155     @Override
read()156     public int read() throws IOException {
157       readCalls++;
158       return super.read();
159     }
160 
161     @Override
read(byte[] b)162     public int read(byte[] b) throws IOException {
163       readCalls++;
164       return super.read(b, 0, Math.min(b.length, blockSize));
165     }
166 
167     @Override
read(byte[] b, int off, int len)168     public int read(byte[] b, int off, int len) throws IOException {
169       readCalls++;
170       return super.read(b, off, Math.min(len, blockSize));
171     }
172 
173     @Override
skip(long len)174     public long skip(long len) throws IOException {
175       skipCalls++;
176       return super.skip(Math.min(len, blockSize));
177     }
178   }
179 
assertDataConsumed(String msg, byte[] data, CodedInputStream input)180   private void assertDataConsumed(String msg, byte[] data, CodedInputStream input)
181       throws IOException {
182     assertWithMessage(msg).that(data).hasLength(input.getTotalBytesRead());
183     assertWithMessage(msg).that(input.isAtEnd()).isTrue();
184   }
185 
186   /**
187    * Parses the given bytes using readRawVarint32() and readRawVarint64() and checks that the result
188    * matches the given value.
189    */
assertReadVarint(byte[] data, long value)190   private void assertReadVarint(byte[] data, long value) throws Exception {
191     for (InputType inputType : InputType.values()) {
192       // Try different block sizes.
193       for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
194         CodedInputStream input = inputType.newDecoder(data, blockSize);
195         assertWithMessage(inputType.name()).that(input.readRawVarint32()).isEqualTo((int) value);
196         assertDataConsumed(inputType.name(), data, input);
197 
198         input = inputType.newDecoder(data, blockSize);
199         assertWithMessage(inputType.name()).that(input.readRawVarint64()).isEqualTo(value);
200         assertDataConsumed(inputType.name(), data, input);
201 
202         input = inputType.newDecoder(data, blockSize);
203         assertWithMessage(inputType.name()).that(input.readRawVarint64SlowPath()).isEqualTo(value);
204         assertDataConsumed(inputType.name(), data, input);
205 
206         input = inputType.newDecoder(data, blockSize);
207         assertWithMessage(inputType.name())
208             .that(input.skipField(WireFormat.WIRETYPE_VARINT))
209             .isTrue();
210         assertDataConsumed(inputType.name(), data, input);
211       }
212     }
213 
214     // Try reading direct from an InputStream.  We want to verify that it
215     // doesn't read past the end of the input, so we copy to a new, bigger
216     // array first.
217     byte[] longerData = new byte[data.length + 1];
218     System.arraycopy(data, 0, longerData, 0, data.length);
219     InputStream rawInput = new ByteArrayInputStream(longerData);
220     assertThat(CodedInputStream.readRawVarint32(rawInput)).isEqualTo((int) value);
221     assertThat(rawInput.available()).isEqualTo(1);
222   }
223 
224   /**
225    * Parses the given bytes using readRawVarint32() and readRawVarint64() and expects them to fail
226    * with an InvalidProtocolBufferException whose description matches the given one.
227    */
assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)228   private void assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
229       throws Exception {
230     for (InputType inputType : InputType.values()) {
231       try {
232         CodedInputStream input = inputType.newDecoder(data);
233         input.readRawVarint32();
234         assertWithMessage("%s: Should have thrown an exception.", inputType.name()).fail();
235       } catch (InvalidProtocolBufferException e) {
236         assertWithMessage(inputType.name())
237             .that(e)
238             .hasMessageThat()
239             .isEqualTo(expected.getMessage());
240       }
241       try {
242         CodedInputStream input = inputType.newDecoder(data);
243         input.readRawVarint64();
244         assertWithMessage("%s: Should have thrown an exception.", inputType.name()).fail();
245       } catch (InvalidProtocolBufferException e) {
246         assertWithMessage(inputType.name())
247             .that(e)
248             .hasMessageThat()
249             .isEqualTo(expected.getMessage());
250       }
251     }
252 
253     // Make sure we get the same error when reading direct from an InputStream.
254     try {
255       CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
256       assertWithMessage("Should have thrown an exception.").fail();
257     } catch (InvalidProtocolBufferException e) {
258       assertThat(e).hasMessageThat().isEqualTo(expected.getMessage());
259     }
260   }
261 
262   /** Tests readRawVarint32() and readRawVarint64(). */
263   @Test
testReadVarint()264   public void testReadVarint() throws Exception {
265     assertReadVarint(bytes(0x00), 0);
266     assertReadVarint(bytes(0x01), 1);
267     assertReadVarint(bytes(0x7f), 127);
268     // 14882
269     assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
270     // 2961488830
271     assertReadVarint(
272         bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
273         (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28));
274 
275     // 64-bit
276     // 7256456126
277     assertReadVarint(
278         bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
279         (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28));
280     // 41256202580718336
281     assertReadVarint(
282         bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
283         (0x00 << 0)
284             | (0x66 << 7)
285             | (0x6b << 14)
286             | (0x1c << 21)
287             | (0x43L << 28)
288             | (0x49L << 35)
289             | (0x24L << 42)
290             | (0x49L << 49));
291     // 11964378330978735131
292     assertReadVarint(
293         bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
294         (0x1b << 0)
295             | (0x28 << 7)
296             | (0x79 << 14)
297             | (0x42 << 21)
298             | (0x3bL << 28)
299             | (0x56L << 35)
300             | (0x00L << 42)
301             | (0x05L << 49)
302             | (0x26L << 56)
303             | (0x01L << 63));
304 
305     // Failures
306     assertReadVarintFailure(
307         InvalidProtocolBufferException.malformedVarint(),
308         bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00));
309     assertReadVarintFailure(InvalidProtocolBufferException.truncatedMessage(), bytes(0x80));
310   }
311 
312   /**
313    * Parses the given bytes using readRawLittleEndian32() and checks that the result matches the
314    * given value.
315    */
assertReadLittleEndian32(byte[] data, int value)316   private void assertReadLittleEndian32(byte[] data, int value) throws Exception {
317     for (InputType inputType : InputType.values()) {
318       // Try different block sizes.
319       for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
320         CodedInputStream input = inputType.newDecoder(data, blockSize);
321         assertWithMessage(inputType.name()).that(input.readRawLittleEndian32()).isEqualTo(value);
322         assertWithMessage(inputType.name()).that(input.isAtEnd()).isTrue();
323       }
324     }
325   }
326 
327   /**
328    * Parses the given bytes using readRawLittleEndian64() and checks that the result matches the
329    * given value.
330    */
assertReadLittleEndian64(byte[] data, long value)331   private void assertReadLittleEndian64(byte[] data, long value) throws Exception {
332     for (InputType inputType : InputType.values()) {
333       // Try different block sizes.
334       for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
335         CodedInputStream input = inputType.newDecoder(data, blockSize);
336         assertWithMessage(inputType.name()).that(input.readRawLittleEndian64()).isEqualTo(value);
337         assertWithMessage(inputType.name()).that(input.isAtEnd()).isTrue();
338       }
339     }
340   }
341 
342   /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
343   @Test
testReadLittleEndian()344   public void testReadLittleEndian() throws Exception {
345     assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
346     assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
347 
348     assertReadLittleEndian64(
349         bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L);
350     assertReadLittleEndian64(
351         bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L);
352   }
353 
354   /** Test decodeZigZag32() and decodeZigZag64(). */
355   @Test
testDecodeZigZag()356   public void testDecodeZigZag() throws Exception {
357     assertThat(CodedInputStream.decodeZigZag32(0)).isEqualTo(0);
358     assertThat(CodedInputStream.decodeZigZag32(1)).isEqualTo(-1);
359     assertThat(CodedInputStream.decodeZigZag32(2)).isEqualTo(1);
360     assertThat(CodedInputStream.decodeZigZag32(3)).isEqualTo(-2);
361     assertThat(CodedInputStream.decodeZigZag32(0x7FFFFFFE)).isEqualTo(0x3FFFFFFF);
362     assertThat(CodedInputStream.decodeZigZag32(0x7FFFFFFF)).isEqualTo(0xC0000000);
363     assertThat(CodedInputStream.decodeZigZag32(0xFFFFFFFE)).isEqualTo(0x7FFFFFFF);
364     assertThat(CodedInputStream.decodeZigZag32(0xFFFFFFFF)).isEqualTo(0x80000000);
365 
366     assertThat(CodedInputStream.decodeZigZag64(0)).isEqualTo(0);
367     assertThat(CodedInputStream.decodeZigZag64(1)).isEqualTo(-1);
368     assertThat(CodedInputStream.decodeZigZag64(2)).isEqualTo(1);
369     assertThat(CodedInputStream.decodeZigZag64(3)).isEqualTo(-2);
370     assertThat(CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL)).isEqualTo(0x000000003FFFFFFFL);
371     assertThat(CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL)).isEqualTo(0xFFFFFFFFC0000000L);
372     assertThat(CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL)).isEqualTo(0x000000007FFFFFFFL);
373     assertThat(CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL)).isEqualTo(0xFFFFFFFF80000000L);
374     assertThat(CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL)).isEqualTo(0x7FFFFFFFFFFFFFFFL);
375     assertThat(CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL)).isEqualTo(0x8000000000000000L);
376   }
377 
378   /** Tests reading and parsing a whole message with every field type. */
379   @Test
testReadWholeMessage()380   public void testReadWholeMessage() throws Exception {
381     TestAllTypes message = TestUtil.getAllSet();
382 
383     byte[] rawBytes = message.toByteArray();
384     assertThat(rawBytes).hasLength(message.getSerializedSize());
385 
386     for (InputType inputType : InputType.values()) {
387       // Try different block sizes.
388       for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
389         TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(rawBytes, blockSize));
390         TestUtil.assertAllFieldsSet(message2);
391       }
392     }
393   }
394 
395   /** Tests skipField(). */
396   @Test
testSkipWholeMessage()397   public void testSkipWholeMessage() throws Exception {
398     TestAllTypes message = TestUtil.getAllSet();
399     byte[] rawBytes = message.toByteArray();
400 
401     InputType[] inputTypes = InputType.values();
402     CodedInputStream[] inputs = new CodedInputStream[inputTypes.length];
403     for (int i = 0; i < inputs.length; ++i) {
404       inputs[i] = inputTypes[i].newDecoder(rawBytes);
405     }
406     UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
407 
408     while (true) {
409       CodedInputStream input1 = inputs[0];
410       int tag = input1.readTag();
411       // Ensure that the rest match.
412       for (int i = 1; i < inputs.length; ++i) {
413         assertWithMessage(inputTypes[i].name()).that(inputs[i].readTag()).isEqualTo(tag);
414       }
415       if (tag == 0) {
416         break;
417       }
418       unknownFields.mergeFieldFrom(tag, input1);
419       // Skip the field for the rest of the inputs.
420       for (int i = 1; i < inputs.length; ++i) {
421         inputs[i].skipField(tag);
422       }
423     }
424   }
425 
426   /**
427    * Test that a bug in skipRawBytes() has been fixed: if the skip skips exactly up to a limit, this
428    * should not break things.
429    */
430   @Test
testSkipRawBytesBug()431   public void testSkipRawBytesBug() throws Exception {
432     byte[] rawBytes = new byte[] {1, 2};
433     for (InputType inputType : InputType.values()) {
434       CodedInputStream input = inputType.newDecoder(rawBytes);
435       int limit = input.pushLimit(1);
436       input.skipRawBytes(1);
437       input.popLimit(limit);
438       assertWithMessage(inputType.name()).that(input.readRawByte()).isEqualTo(2);
439     }
440   }
441 
442   /**
443    * Test that a bug in skipRawBytes() has been fixed: if the skip skips past the end of a buffer
444    * with a limit that has been set past the end of that buffer, this should not break things.
445    */
446   @Test
testSkipRawBytesPastEndOfBufferWithLimit()447   public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
448     byte[] rawBytes = new byte[] {1, 2, 3, 4, 5};
449     for (InputType inputType : InputType.values()) {
450       CodedInputStream input = inputType.newDecoder(rawBytes);
451       int limit = input.pushLimit(4);
452       // In order to expose the bug we need to read at least one byte to prime the
453       // buffer inside the CodedInputStream.
454       assertWithMessage(inputType.name()).that(input.readRawByte()).isEqualTo(1);
455       // Skip to the end of the limit.
456       input.skipRawBytes(3);
457       assertWithMessage(inputType.name()).that(input.isAtEnd()).isTrue();
458       input.popLimit(limit);
459       assertWithMessage(inputType.name()).that(input.readRawByte()).isEqualTo(5);
460     }
461   }
462 
463   /**
464    * Test that calling skipRawBytes (when not merging a message) actually skips from the underlying
465    * inputstream, regardless of the buffer size used.
466    */
467   @Test
testSkipRawBytesActuallySkips()468   public void testSkipRawBytesActuallySkips() throws Exception {
469     SmallBlockInputStream bytes = new SmallBlockInputStream(new byte[] {1, 2, 3, 4, 5}, 3);
470     CodedInputStream input = CodedInputStream.newInstance(bytes, 1); // Tiny buffer
471     input.skipRawBytes(3);
472     input.skipRawBytes(2);
473     assertThat(bytes.skipCalls).isEqualTo(2);
474     assertThat(bytes.readCalls).isEqualTo(0);
475   }
476 
477   @Test
testSkipHugeBlob()478   public void testSkipHugeBlob() throws Exception {
479     // Allocate and initialize a 1MB blob.
480     int blobSize = 1 << 20;
481     byte[] blob = new byte[blobSize];
482     for (int i = 0; i < blob.length; i++) {
483       blob[i] = (byte) i;
484     }
485 
486     for (InputType inputType : InputType.values()) {
487       CodedInputStream decoder = inputType.newDecoder(blob);
488       decoder.skipRawBytes(blobSize - 10);
489       byte[] remaining = decoder.readRawBytes(10);
490       assertArrayEquals(Arrays.copyOfRange(blob, blobSize - 10, blobSize), remaining);
491     }
492   }
493 
494   /** Skipping a huge blob should not allocate excessive memory, so there should be no limit */
495   @Test
testSkipMaliciouslyHugeBlob()496   public void testSkipMaliciouslyHugeBlob() throws Exception {
497     InputStream is = new RepeatingInputStream(new byte[] {1}, Integer.MAX_VALUE);
498     CodedInputStream.newInstance(is).skipRawBytes(Integer.MAX_VALUE);
499   }
500 
501   @Test
testReadHugeBlob()502   public void testReadHugeBlob() throws Exception {
503     // Allocate and initialize a 1MB blob.
504     byte[] blob = new byte[1 << 20];
505     for (int i = 0; i < blob.length; i++) {
506       blob[i] = (byte) i;
507     }
508 
509     // Make a message containing it.
510     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
511     TestUtil.setAllFields(builder);
512     builder.setOptionalBytes(ByteString.copyFrom(blob));
513     TestAllTypes message = builder.build();
514 
515     byte[] data = message.toByteArray();
516     for (InputType inputType : InputType.values()) {
517       // Serialize and parse it.  Make sure to parse from an InputStream, not
518       // directly from a ByteString, so that CodedInputStream uses buffered
519       // reading.
520       TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(data));
521 
522       assertWithMessage(inputType.name())
523           .that(message.getOptionalBytes())
524           .isEqualTo(message2.getOptionalBytes());
525 
526       // Make sure all the other fields were parsed correctly.
527       TestAllTypes message3 =
528           TestAllTypes.newBuilder(message2)
529               .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
530               .build();
531       TestUtil.assertAllFieldsSet(message3);
532     }
533   }
534 
535   @Test
testReadMaliciouslyLargeBlob()536   public void testReadMaliciouslyLargeBlob() throws Exception {
537     ByteString.Output rawOutput = ByteString.newOutput();
538     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
539 
540     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
541     output.writeUInt32NoTag(tag);
542     output.writeUInt32NoTag(0x7FFFFFFF);
543     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
544     output.flush();
545 
546     byte[] data = rawOutput.toByteString().toByteArray();
547     for (InputType inputType : InputType.values()) {
548       CodedInputStream input = inputType.newDecoder(data);
549       assertThat(input.readTag()).isEqualTo(tag);
550       try {
551         input.readBytes();
552         assertWithMessage("%s: Should have thrown an exception!", inputType.name()).fail();
553       } catch (InvalidProtocolBufferException e) {
554         // success.
555       }
556     }
557   }
558 
559   @Test
testReadStringWithSizeOverflow_throwsInvalidProtocolBufferException()560   public void testReadStringWithSizeOverflow_throwsInvalidProtocolBufferException()
561       throws Exception {
562     ByteString.Output rawOutput = ByteString.newOutput();
563     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
564 
565     output.writeUInt32NoTag(0xFFFFFFFF); // Larger than Integer.MAX_VALUE.
566     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
567     output.flush();
568     byte[] data = rawOutput.toByteString().toByteArray();
569     for (InputType inputType : InputType.values()) {
570       CodedInputStream input = inputType.newDecoder(data);
571       assertThrows(InvalidProtocolBufferException.class, input::readString);
572     }
573   }
574 
575   @Test
testReadStringRequireUtf8WithSizeOverflow_throwsInvalidProtocolBufferException()576   public void testReadStringRequireUtf8WithSizeOverflow_throwsInvalidProtocolBufferException()
577       throws Exception {
578     ByteString.Output rawOutput = ByteString.newOutput();
579     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
580 
581     output.writeUInt32NoTag(0xFFFFFFFF); // Larger than Integer.MAX_VALUE.
582     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
583     output.flush();
584     byte[] data = rawOutput.toByteString().toByteArray();
585     for (InputType inputType : InputType.values()) {
586       CodedInputStream input = inputType.newDecoder(data);
587       assertThrows(InvalidProtocolBufferException.class, input::readStringRequireUtf8);
588     }
589   }
590 
591   @Test
testReadBytesWithHugeSizeOverflow_throwsInvalidProtocolBufferException()592   public void testReadBytesWithHugeSizeOverflow_throwsInvalidProtocolBufferException()
593       throws Exception {
594     ByteString.Output rawOutput = ByteString.newOutput();
595     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
596 
597     output.writeUInt32NoTag(0xFFFFFFFF); // Larger than Integer.MAX_VALUE.
598     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
599     output.flush();
600     byte[] data = rawOutput.toByteString().toByteArray();
601     for (InputType inputType : InputType.values()) {
602       CodedInputStream input = inputType.newDecoder(data);
603       assertThrows(InvalidProtocolBufferException.class, input::readBytes);
604     }
605   }
606 
607   @Test
testReadByteArrayWithHugeSizeOverflow_throwsInvalidProtocolBufferException()608   public void testReadByteArrayWithHugeSizeOverflow_throwsInvalidProtocolBufferException()
609       throws Exception {
610     ByteString.Output rawOutput = ByteString.newOutput();
611     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
612 
613     output.writeUInt32NoTag(0xFFFFFFFF); // Larger than Integer.MAX_VALUE.
614     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
615     output.flush();
616     byte[] data = rawOutput.toByteString().toByteArray();
617     for (InputType inputType : InputType.values()) {
618       CodedInputStream input = inputType.newDecoder(data);
619       assertThrows(InvalidProtocolBufferException.class, input::readByteArray);
620     }
621   }
622 
623   @Test
testReadByteBufferWithSizeOverflow_throwsInvalidProtocolBufferException()624   public void testReadByteBufferWithSizeOverflow_throwsInvalidProtocolBufferException()
625       throws Exception {
626     ByteString.Output rawOutput = ByteString.newOutput();
627     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
628 
629     output.writeUInt32NoTag(0xFFFFFFFF); // Larger than Integer.MAX_VALUE.
630     output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
631     output.flush();
632     byte[] data = rawOutput.toByteString().toByteArray();
633     for (InputType inputType : InputType.values()) {
634       CodedInputStream input = inputType.newDecoder(data);
635       assertThrows(InvalidProtocolBufferException.class, input::readByteBuffer);
636     }
637   }
638 
639   /**
640    * Test we can do messages that are up to CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G or
641    * Integer#MAX_SIZE).
642    *
643    * @throws IOException
644    */
645   @Test
testParseMessagesCloseTo2G()646   public void testParseMessagesCloseTo2G() throws IOException {
647     byte[] serializedMessage = getBigSerializedMessage();
648     // How many of these big messages do we need to take us near our 2G limit?
649     int count = Integer.MAX_VALUE / serializedMessage.length;
650     // Now make an inputstream that will fake a near 2G message of messages
651     // returning our big serialized message 'count' times.
652     InputStream is = new RepeatingInputStream(serializedMessage, count);
653     // Parse should succeed!
654     TestAllTypes unused = TestAllTypes.parseFrom(is);
655   }
656 
657   /**
658    * Test there is an exception if a message exceeds CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G
659    * or Integer#MAX_SIZE).
660    *
661    * @throws IOException
662    */
663   @Test
testParseMessagesOver2G()664   public void testParseMessagesOver2G() throws IOException {
665     byte[] serializedMessage = getBigSerializedMessage();
666     // How many of these big messages do we need to take us near our 2G limit?
667     int count = Integer.MAX_VALUE / serializedMessage.length;
668     // Now add one to take us over the limit
669     count++;
670     // Now make an inputstream that will fake a near 2G message of messages
671     // returning our big serialized message 'count' times.
672     InputStream is = new RepeatingInputStream(serializedMessage, count);
673     try {
674       TestAllTypes.parseFrom(is);
675       assertWithMessage("Should have thrown an exception!").fail();
676     } catch (InvalidProtocolBufferException e) {
677       assertThat(e).hasMessageThat().contains("too large");
678     }
679   }
680 
681   /*
682    * @return A serialized big message.
683    */
getBigSerializedMessage()684   private static byte[] getBigSerializedMessage() {
685     byte[] value = new byte[16 * 1024 * 1024];
686     ByteString bsValue = ByteString.wrap(value);
687     return TestAllTypes.newBuilder().setOptionalBytes(bsValue).build().toByteArray();
688   }
689 
690   /*
691    * An input stream that repeats a byte arrays' content a number of times.
692    * Simulates really large input without consuming loads of memory. Used above
693    * to test the parsing behavior when the input size exceeds 2G or close to it.
694    */
695   private static class RepeatingInputStream extends InputStream {
696     private final byte[] serializedMessage;
697     private final int count;
698     private int index = 0;
699     private int offset = 0;
700 
RepeatingInputStream(byte[] serializedMessage, int count)701     RepeatingInputStream(byte[] serializedMessage, int count) {
702       this.serializedMessage = serializedMessage;
703       this.count = count;
704     }
705 
706     @Override
read()707     public int read() throws IOException {
708       if (this.offset == this.serializedMessage.length) {
709         this.index++;
710         this.offset = 0;
711       }
712       if (this.index == this.count) {
713         return -1;
714       }
715       return this.serializedMessage[offset++];
716     }
717   }
718 
makeRecursiveMessage(int depth)719   private TestRecursiveMessage makeRecursiveMessage(int depth) {
720     if (depth == 0) {
721       return TestRecursiveMessage.newBuilder().setI(5).build();
722     } else {
723       return TestRecursiveMessage.newBuilder().setA(makeRecursiveMessage(depth - 1)).build();
724     }
725   }
726 
assertMessageDepth(String msg, TestRecursiveMessage message, int depth)727   private void assertMessageDepth(String msg, TestRecursiveMessage message, int depth) {
728     if (depth == 0) {
729       assertWithMessage(msg).that(message.hasA()).isFalse();
730       assertWithMessage(msg).that(message.getI()).isEqualTo(5);
731     } else {
732       assertWithMessage(msg).that(message.hasA()).isTrue();
733       assertMessageDepth(msg, message.getA(), depth - 1);
734     }
735   }
736 
737   @Test
testMaliciousRecursion()738   public void testMaliciousRecursion() throws Exception {
739     byte[] data100 = makeRecursiveMessage(100).toByteArray();
740     byte[] data101 = makeRecursiveMessage(101).toByteArray();
741 
742     for (InputType inputType : InputType.values()) {
743       assertMessageDepth(
744           inputType.name(), TestRecursiveMessage.parseFrom(inputType.newDecoder(data100)), 100);
745 
746       try {
747         TestRecursiveMessage.parseFrom(inputType.newDecoder(data101));
748         assertWithMessage("Should have thrown an exception!").fail();
749       } catch (InvalidProtocolBufferException e) {
750         // success.
751       }
752 
753       CodedInputStream input = inputType.newDecoder(data100);
754       input.setRecursionLimit(8);
755       try {
756         TestRecursiveMessage.parseFrom(input);
757         assertWithMessage("%s: Should have thrown an exception!", inputType.name()).fail();
758       } catch (InvalidProtocolBufferException e) {
759         // success.
760       }
761     }
762   }
763 
764   @Test
testMaliciousRecursion_unknownFields()765   public void testMaliciousRecursion_unknownFields() throws Exception {
766     Throwable thrown =
767         assertThrows(
768             InvalidProtocolBufferException.class,
769             () -> TestRecursiveMessage.parseFrom(NESTING_SGROUP));
770 
771     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
772   }
773 
774   @Test
testMaliciousRecursion_skippingUnknownField()775   public void testMaliciousRecursion_skippingUnknownField() throws Exception {
776     Throwable thrown =
777         assertThrows(
778             InvalidProtocolBufferException.class,
779             () ->
780                 DiscardUnknownFieldsParser.wrap(TestRecursiveMessage.parser())
781                     .parseFrom(NESTING_SGROUP));
782 
783     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
784   }
785 
786   @Test
testMaliciousSGroupTagsWithMapField_fromInputStream()787   public void testMaliciousSGroupTagsWithMapField_fromInputStream() throws Exception {
788     Throwable parseFromThrown =
789         assertThrows(
790             InvalidProtocolBufferException.class,
791             () ->
792                 MapContainer.parseFrom(
793                     new ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
794     Throwable mergeFromThrown =
795         assertThrows(
796             InvalidProtocolBufferException.class,
797             () ->
798                 MapContainer.newBuilder()
799                     .mergeFrom(new ByteArrayInputStream(NESTING_SGROUP_WITH_INITIAL_BYTES)));
800 
801     assertThat(parseFromThrown)
802         .hasMessageThat()
803         .contains("Protocol message had too many levels of nesting");
804     assertThat(mergeFromThrown)
805         .hasMessageThat()
806         .contains("Protocol message had too many levels of nesting");
807   }
808 
809   @Test
testMaliciousSGroupTags_inputStream_skipMessage()810   public void testMaliciousSGroupTags_inputStream_skipMessage() throws Exception {
811     ByteArrayInputStream inputSteam = new ByteArrayInputStream(NESTING_SGROUP);
812     CodedInputStream input = CodedInputStream.newInstance(inputSteam);
813     CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
814 
815     Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
816     Throwable thrown2 =
817         assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
818 
819     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
820     assertThat(thrown2)
821         .hasMessageThat()
822         .contains("Protocol message had too many levels of nesting");
823   }
824 
825   @Test
testMaliciousSGroupTagsWithMapField_fromByteArray()826   public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws Exception {
827     Throwable parseFromThrown =
828         assertThrows(
829             InvalidProtocolBufferException.class,
830             () -> MapContainer.parseFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
831     Throwable mergeFromThrown =
832         assertThrows(
833             InvalidProtocolBufferException.class,
834             () -> MapContainer.newBuilder().mergeFrom(NESTING_SGROUP_WITH_INITIAL_BYTES));
835 
836     assertThat(parseFromThrown)
837         .hasMessageThat()
838         .contains("the input ended unexpectedly in the middle of a field");
839     assertThat(mergeFromThrown)
840         .hasMessageThat()
841         .contains("the input ended unexpectedly in the middle of a field");
842   }
843 
844   @Test
testMaliciousSGroupTags_arrayDecoder_skipMessage()845   public void testMaliciousSGroupTags_arrayDecoder_skipMessage() throws Exception {
846     CodedInputStream input = CodedInputStream.newInstance(NESTING_SGROUP);
847     CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
848 
849     Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
850     Throwable thrown2 =
851         assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
852 
853     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
854     assertThat(thrown2)
855         .hasMessageThat()
856         .contains("Protocol message had too many levels of nesting");
857   }
858 
859   @Test
testMaliciousSGroupTagsWithMapField_fromByteBuffer()860   public void testMaliciousSGroupTagsWithMapField_fromByteBuffer() throws Exception {
861     Throwable thrown =
862         assertThrows(
863             InvalidProtocolBufferException.class,
864             () -> MapContainer.parseFrom(ByteBuffer.wrap(NESTING_SGROUP_WITH_INITIAL_BYTES)));
865 
866     assertThat(thrown)
867         .hasMessageThat()
868         .contains("the input ended unexpectedly in the middle of a field");
869   }
870 
871   @Test
testMaliciousSGroupTags_byteBuffer_skipMessage()872   public void testMaliciousSGroupTags_byteBuffer_skipMessage() throws Exception {
873     CodedInputStream input = InputType.NIO_DIRECT.newDecoder(NESTING_SGROUP);
874     CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
875 
876     Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
877     Throwable thrown2 =
878         assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
879 
880     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
881     assertThat(thrown2)
882         .hasMessageThat()
883         .contains("Protocol message had too many levels of nesting");
884   }
885 
886   @Test
testMaliciousSGroupTags_iterableByteBuffer()887   public void testMaliciousSGroupTags_iterableByteBuffer() throws Exception {
888     CodedInputStream input = InputType.ITER_DIRECT.newDecoder(NESTING_SGROUP);
889     CodedOutputStream output = CodedOutputStream.newInstance(new byte[NESTING_SGROUP.length]);
890 
891     Throwable thrown = assertThrows(InvalidProtocolBufferException.class, input::skipMessage);
892     Throwable thrown2 =
893         assertThrows(InvalidProtocolBufferException.class, () -> input.skipMessage(output));
894 
895     assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting");
896     assertThat(thrown2)
897         .hasMessageThat()
898         .contains("Protocol message had too many levels of nesting");
899   }
900 
checkSizeLimitExceeded(InvalidProtocolBufferException e)901   private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
902     assertThat(e)
903         .hasMessageThat()
904         .isEqualTo(InvalidProtocolBufferException.sizeLimitExceeded().getMessage());
905   }
906 
907   @Test
testSizeLimit()908   public void testSizeLimit() throws Exception {
909     // NOTE: Size limit only applies to the stream-backed CIS.
910     CodedInputStream input =
911         CodedInputStream.newInstance(
912             new SmallBlockInputStream(TestUtil.getAllSet().toByteArray(), 16));
913     input.setSizeLimit(16);
914 
915     try {
916       TestAllTypes.parseFrom(input);
917       assertWithMessage("Should have thrown an exception!").fail();
918     } catch (InvalidProtocolBufferException expected) {
919       checkSizeLimitExceeded(expected);
920     }
921   }
922 
923   @Test
testResetSizeCounter()924   public void testResetSizeCounter() throws Exception {
925     // NOTE: Size limit only applies to the stream-backed CIS.
926     CodedInputStream input =
927         CodedInputStream.newInstance(new SmallBlockInputStream(new byte[256], 8));
928     input.setSizeLimit(16);
929     input.readRawBytes(16);
930     assertThat(input.getTotalBytesRead()).isEqualTo(16);
931 
932     try {
933       input.readRawByte();
934       assertWithMessage("Should have thrown an exception!").fail();
935     } catch (InvalidProtocolBufferException expected) {
936       checkSizeLimitExceeded(expected);
937     }
938 
939     input.resetSizeCounter();
940     assertThat(input.getTotalBytesRead()).isEqualTo(0);
941     input.readRawByte(); // No exception thrown.
942     input.resetSizeCounter();
943     assertThat(input.getTotalBytesRead()).isEqualTo(0);
944     input.readRawBytes(16);
945     assertThat(input.getTotalBytesRead()).isEqualTo(16);
946     input.resetSizeCounter();
947 
948     try {
949       input.readRawBytes(17); // Hits limit again.
950       assertWithMessage("Should have thrown an exception!").fail();
951     } catch (InvalidProtocolBufferException expected) {
952       checkSizeLimitExceeded(expected);
953     }
954   }
955 
956   @Test
testRefillBufferWithCorrectSize()957   public void testRefillBufferWithCorrectSize() throws Exception {
958     // NOTE: refillBuffer only applies to the stream-backed CIS.
959     byte[] bytes = "123456789".getBytes("UTF-8");
960     ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
961     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
962 
963     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
964     output.writeUInt32NoTag(tag);
965     output.writeUInt32NoTag(bytes.length);
966     output.writeRawBytes(bytes);
967     output.writeUInt32NoTag(tag);
968     output.writeUInt32NoTag(bytes.length);
969     output.writeRawBytes(bytes);
970     output.writeRawByte(4);
971     output.flush();
972 
973     // Input is two string with length 9 and one raw byte.
974     byte[] rawInput = rawOutput.toByteArray();
975     for (int inputStreamBufferLength = 8;
976         inputStreamBufferLength <= rawInput.length + 1;
977         inputStreamBufferLength++) {
978       CodedInputStream input =
979           CodedInputStream.newInstance(new ByteArrayInputStream(rawInput), inputStreamBufferLength);
980       input.setSizeLimit(rawInput.length - 1);
981       input.readString();
982       input.readString();
983       try {
984         input.readRawByte(); // Hits limit.
985         assertWithMessage("Should have thrown an exception!").fail();
986       } catch (InvalidProtocolBufferException expected) {
987         checkSizeLimitExceeded(expected);
988       }
989     }
990   }
991 
992   @Test
testIsAtEnd()993   public void testIsAtEnd() throws Exception {
994     CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(new byte[5]));
995     try {
996       for (int i = 0; i < 5; i++) {
997         assertThat(input.isAtEnd()).isFalse();
998         input.readRawByte();
999       }
1000       assertThat(input.isAtEnd()).isTrue();
1001     } catch (Exception e) {
1002       throw new AssertionError("Catch exception in the testIsAtEnd", e);
1003     }
1004   }
1005 
1006   @Test
testCurrentLimitExceeded()1007   public void testCurrentLimitExceeded() throws Exception {
1008     byte[] bytes = "123456789".getBytes("UTF-8");
1009     ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
1010     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
1011 
1012     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
1013     output.writeUInt32NoTag(tag);
1014     output.writeUInt32NoTag(bytes.length);
1015     output.writeRawBytes(bytes);
1016     output.flush();
1017 
1018     byte[] rawInput = rawOutput.toByteArray();
1019     CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(rawInput));
1020     // The length of the whole rawInput
1021     input.setSizeLimit(11);
1022     // Some number that is smaller than the rawInput's length
1023     // but larger than 2
1024     input.pushLimit(5);
1025     try {
1026       input.readString();
1027       assertWithMessage("Should have thrown an exception").fail();
1028     } catch (InvalidProtocolBufferException expected) {
1029       assertThat(expected)
1030           .hasMessageThat()
1031           .isEqualTo(InvalidProtocolBufferException.truncatedMessage().getMessage());
1032     }
1033   }
1034 
1035   @Test
testSizeLimitMultipleMessages()1036   public void testSizeLimitMultipleMessages() throws Exception {
1037     // NOTE: Size limit only applies to the stream-backed CIS.
1038     byte[] bytes = new byte[256];
1039     for (int i = 0; i < bytes.length; i++) {
1040       bytes[i] = (byte) i;
1041     }
1042     CodedInputStream input = CodedInputStream.newInstance(new SmallBlockInputStream(bytes, 7));
1043     input.setSizeLimit(16);
1044     for (int i = 0; i < 256 / 16; i++) {
1045       byte[] message = input.readRawBytes(16);
1046       for (int j = 0; j < message.length; j++) {
1047         assertThat(message[j] & 0xff).isEqualTo(i * 16 + j);
1048       }
1049       assertThat(input.getTotalBytesRead()).isEqualTo(16);
1050       input.resetSizeCounter();
1051       assertThat(input.getTotalBytesRead()).isEqualTo(0);
1052     }
1053   }
1054 
1055   @Test
testReadString()1056   public void testReadString() throws Exception {
1057     String lorem = "Lorem ipsum dolor sit amet ";
1058     StringBuilder builder = new StringBuilder();
1059     for (int i = 0; i < 4096; i += lorem.length()) {
1060       builder.append(lorem);
1061     }
1062     lorem = builder.toString().substring(0, 4096);
1063     byte[] bytes = lorem.getBytes("UTF-8");
1064     ByteString.Output rawOutput = ByteString.newOutput();
1065     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
1066 
1067     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
1068     output.writeUInt32NoTag(tag);
1069     output.writeUInt32NoTag(bytes.length);
1070     output.writeRawBytes(bytes);
1071     output.flush();
1072 
1073     byte[] rawInput = rawOutput.toByteString().toByteArray();
1074     for (InputType inputType : InputType.values()) {
1075       CodedInputStream input = inputType.newDecoder(rawInput);
1076       assertWithMessage(inputType.name()).that(tag).isEqualTo(input.readTag());
1077       String text = input.readString();
1078       assertWithMessage(inputType.name()).that(lorem).isEqualTo(text);
1079     }
1080   }
1081 
1082   @Test
testReadStringRequireUtf8()1083   public void testReadStringRequireUtf8() throws Exception {
1084     String lorem = "Lorem ipsum dolor sit amet ";
1085     StringBuilder builder = new StringBuilder();
1086     for (int i = 0; i < 4096; i += lorem.length()) {
1087       builder.append(lorem);
1088     }
1089     lorem = builder.toString().substring(0, 4096);
1090     byte[] bytes = lorem.getBytes("UTF-8");
1091     ByteString.Output rawOutput = ByteString.newOutput();
1092     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
1093 
1094     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
1095     output.writeUInt32NoTag(tag);
1096     output.writeUInt32NoTag(bytes.length);
1097     output.writeRawBytes(bytes);
1098     output.flush();
1099 
1100     byte[] rawInput = rawOutput.toByteString().toByteArray();
1101     for (InputType inputType : InputType.values()) {
1102       CodedInputStream input = inputType.newDecoder(rawInput);
1103       assertWithMessage(inputType.name()).that(tag).isEqualTo(input.readTag());
1104       String text = input.readStringRequireUtf8();
1105       assertWithMessage(inputType.name()).that(lorem).isEqualTo(text);
1106     }
1107   }
1108 
1109   /**
1110    * Tests that if we readString invalid UTF-8 bytes, no exception is thrown. Instead, the invalid
1111    * bytes are replaced with the Unicode "replacement character" U+FFFD.
1112    */
1113   @Test
testReadStringInvalidUtf8()1114   public void testReadStringInvalidUtf8() throws Exception {
1115     ByteString.Output rawOutput = ByteString.newOutput();
1116     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1117 
1118     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
1119     output.writeUInt32NoTag(tag);
1120     output.writeUInt32NoTag(1);
1121     output.writeRawBytes(new byte[] {(byte) 0x80});
1122     output.flush();
1123 
1124     byte[] rawInput = rawOutput.toByteString().toByteArray();
1125     for (InputType inputType : InputType.values()) {
1126       CodedInputStream input = inputType.newDecoder(rawInput);
1127       assertWithMessage(inputType.name()).that(input.readTag()).isEqualTo(tag);
1128       String text = input.readString();
1129       assertWithMessage(inputType.name()).that(text.charAt(0)).isEqualTo(0xfffd);
1130     }
1131   }
1132 
1133   /**
1134    * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an InvalidProtocolBufferException
1135    * is thrown.
1136    */
1137   @Test
testReadStringRequireUtf8InvalidUtf8()1138   public void testReadStringRequireUtf8InvalidUtf8() throws Exception {
1139     ByteString.Output rawOutput = ByteString.newOutput();
1140     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1141 
1142     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
1143     output.writeUInt32NoTag(tag);
1144     output.writeUInt32NoTag(1);
1145     output.writeRawBytes(new byte[] {(byte) 0x80});
1146     output.flush();
1147 
1148     byte[] rawInput = rawOutput.toByteString().toByteArray();
1149     for (InputType inputType : InputType.values()) {
1150       CodedInputStream input = inputType.newDecoder(rawInput);
1151       assertThat(input.readTag()).isEqualTo(tag);
1152       try {
1153         input.readStringRequireUtf8();
1154         assertWithMessage("%s: Expected invalid UTF-8 exception.", inputType.name()).fail();
1155       } catch (InvalidProtocolBufferException exception) {
1156         assertWithMessage(inputType.name())
1157             .that(exception)
1158             .hasMessageThat()
1159             .isEqualTo("Protocol message had invalid UTF-8.");
1160       }
1161     }
1162   }
1163 
1164   @Test
testReadFromSlice()1165   public void testReadFromSlice() throws Exception {
1166     byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
1167     CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
1168     assertThat(in.getTotalBytesRead()).isEqualTo(0);
1169     for (int i = 3; i < 8; i++) {
1170       assertThat(in.readRawByte()).isEqualTo(i);
1171       assertThat(in.getTotalBytesRead()).isEqualTo(i - 2);
1172     }
1173     // eof
1174     assertThat(in.readTag()).isEqualTo(0);
1175     assertThat(in.getTotalBytesRead()).isEqualTo(5);
1176   }
1177 
1178   @Test
testInvalidTag()1179   public void testInvalidTag() throws Exception {
1180     // Any tag number which corresponds to field number zero is invalid and
1181     // should throw InvalidProtocolBufferException.
1182     for (InputType inputType : InputType.values()) {
1183       for (int i = 0; i < 8; i++) {
1184         try {
1185           inputType.newDecoder(bytes(i)).readTag();
1186           assertWithMessage("%s: Should have thrown an exception.", inputType.name()).fail();
1187         } catch (InvalidProtocolBufferException e) {
1188           assertWithMessage(inputType.name())
1189               .that(e)
1190               .hasMessageThat()
1191               .isEqualTo(InvalidProtocolBufferException.invalidTag().getMessage());
1192         }
1193       }
1194     }
1195   }
1196 
1197   @Test
testReadByteArray()1198   public void testReadByteArray() throws Exception {
1199     ByteString.Output rawOutput = ByteString.newOutput();
1200     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1201     // Zero-sized bytes field.
1202     output.writeUInt32NoTag(0);
1203     // One one-byte bytes field
1204     output.writeUInt32NoTag(1);
1205     output.writeRawBytes(new byte[] {(byte) 23});
1206     // Another one-byte bytes field
1207     output.writeUInt32NoTag(1);
1208     output.writeRawBytes(new byte[] {(byte) 45});
1209     // A bytes field large enough that won't fit into the 4K buffer.
1210     final int bytesLength = 16 * 1024;
1211     byte[] bytes = new byte[bytesLength];
1212     bytes[0] = (byte) 67;
1213     bytes[bytesLength - 1] = (byte) 89;
1214     output.writeUInt32NoTag(bytesLength);
1215     output.writeRawBytes(bytes);
1216 
1217     output.flush();
1218 
1219     byte[] rawInput = rawOutput.toByteString().toByteArray();
1220     for (InputType inputType : InputType.values()) {
1221       CodedInputStream inputStream = inputType.newDecoder(rawInput);
1222 
1223       byte[] result = inputStream.readByteArray();
1224       assertWithMessage(inputType.name()).that(result).isEmpty();
1225       result = inputStream.readByteArray();
1226       assertWithMessage(inputType.name()).that(result).hasLength(1);
1227       assertWithMessage(inputType.name()).that(result[0]).isEqualTo((byte) 23);
1228       result = inputStream.readByteArray();
1229       assertWithMessage(inputType.name()).that(result).hasLength(1);
1230       assertWithMessage(inputType.name()).that(result[0]).isEqualTo((byte) 45);
1231       result = inputStream.readByteArray();
1232       assertWithMessage(inputType.name()).that(result).hasLength(bytesLength);
1233       assertWithMessage(inputType.name()).that(result[0]).isEqualTo((byte) 67);
1234       assertWithMessage(inputType.name()).that(result[bytesLength - 1]).isEqualTo((byte) 89);
1235     }
1236   }
1237 
1238   @Test
testReadLargeByteStringFromInputStream()1239   public void testReadLargeByteStringFromInputStream() throws Exception {
1240     byte[] bytes = new byte[1024 * 1024];
1241     for (int i = 0; i < bytes.length; i++) {
1242       bytes[i] = (byte) (i & 0xFF);
1243     }
1244     ByteString.Output rawOutput = ByteString.newOutput();
1245     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1246     output.writeUInt32NoTag(bytes.length);
1247     output.writeRawBytes(bytes);
1248     output.flush();
1249     byte[] data = rawOutput.toByteString().toByteArray();
1250 
1251     CodedInputStream input =
1252         CodedInputStream.newInstance(
1253             new ByteArrayInputStream(data) {
1254               @Override
1255               public synchronized int available() {
1256                 return 0;
1257               }
1258             });
1259     ByteString result = input.readBytes();
1260     assertThat(ByteString.copyFrom(bytes)).isEqualTo(result);
1261   }
1262 
1263   @Test
testReadLargeByteArrayFromInputStream()1264   public void testReadLargeByteArrayFromInputStream() throws Exception {
1265     byte[] bytes = new byte[1024 * 1024];
1266     for (int i = 0; i < bytes.length; i++) {
1267       bytes[i] = (byte) (i & 0xFF);
1268     }
1269     ByteString.Output rawOutput = ByteString.newOutput();
1270     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1271     output.writeUInt32NoTag(bytes.length);
1272     output.writeRawBytes(bytes);
1273     output.flush();
1274     byte[] data = rawOutput.toByteString().toByteArray();
1275 
1276     CodedInputStream input =
1277         CodedInputStream.newInstance(
1278             new ByteArrayInputStream(data) {
1279               @Override
1280               public synchronized int available() {
1281                 return 0;
1282               }
1283             });
1284     byte[] result = input.readByteArray();
1285     assertThat(Arrays.equals(bytes, result)).isTrue();
1286   }
1287 
1288   @Test
testReadByteBuffer()1289   public void testReadByteBuffer() throws Exception {
1290     ByteString.Output rawOutput = ByteString.newOutput();
1291     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
1292     // Zero-sized bytes field.
1293     output.writeUInt32NoTag(0);
1294     // One one-byte bytes field
1295     output.writeUInt32NoTag(1);
1296     output.writeRawBytes(new byte[] {(byte) 23});
1297     // Another one-byte bytes field
1298     output.writeUInt32NoTag(1);
1299     output.writeRawBytes(new byte[] {(byte) 45});
1300     // A bytes field large enough that won't fit into the 4K buffer.
1301     final int bytesLength = 16 * 1024;
1302     byte[] bytes = new byte[bytesLength];
1303     bytes[0] = (byte) 67;
1304     bytes[bytesLength - 1] = (byte) 89;
1305     output.writeUInt32NoTag(bytesLength);
1306     output.writeRawBytes(bytes);
1307 
1308     output.flush();
1309 
1310     byte[] rawInput = rawOutput.toByteString().toByteArray();
1311     for (InputType inputType : InputType.values()) {
1312       CodedInputStream inputStream = inputType.newDecoder(rawInput);
1313 
1314       ByteBuffer result = inputStream.readByteBuffer();
1315       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(0);
1316       result = inputStream.readByteBuffer();
1317       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1318       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 23);
1319       result = inputStream.readByteBuffer();
1320       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1321       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 45);
1322       result = inputStream.readByteBuffer();
1323       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(bytesLength);
1324       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 67);
1325       result.position(bytesLength - 1);
1326       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 89);
1327     }
1328   }
1329 
1330   @Test
testReadByteBufferAliasing()1331   public void testReadByteBufferAliasing() throws Exception {
1332     ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
1333     CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream);
1334     // Zero-sized bytes field.
1335     output.writeUInt32NoTag(0);
1336     // One one-byte bytes field
1337     output.writeUInt32NoTag(1);
1338     output.writeRawBytes(new byte[] {(byte) 23});
1339     // Another one-byte bytes field
1340     output.writeUInt32NoTag(1);
1341     output.writeRawBytes(new byte[] {(byte) 45});
1342     // A bytes field large enough that won't fit into the 4K buffer.
1343     final int bytesLength = 16 * 1024;
1344     byte[] bytes = new byte[bytesLength];
1345     bytes[0] = (byte) 67;
1346     bytes[bytesLength - 1] = (byte) 89;
1347     output.writeUInt32NoTag(bytesLength);
1348     output.writeRawBytes(bytes);
1349     output.flush();
1350 
1351     byte[] data = byteArrayStream.toByteArray();
1352 
1353     for (InputType inputType : InputType.values()) {
1354       if (inputType == InputType.STREAM
1355           || inputType == InputType.STREAM_ITER_DIRECT
1356           || inputType == InputType.ITER_DIRECT) {
1357         // Aliasing doesn't apply to stream-backed CIS.
1358         continue;
1359       }
1360 
1361       // Without aliasing
1362       CodedInputStream inputStream = inputType.newDecoder(data);
1363       ByteBuffer result = inputStream.readByteBuffer();
1364       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(0);
1365       result = inputStream.readByteBuffer();
1366       assertWithMessage(inputType.name()).that(result.array() != data).isTrue();
1367       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1368       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 23);
1369       result = inputStream.readByteBuffer();
1370       assertWithMessage(inputType.name()).that(result.array() != data).isTrue();
1371       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1372       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 45);
1373       result = inputStream.readByteBuffer();
1374       assertWithMessage(inputType.name()).that(result.array() != data).isTrue();
1375       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(bytesLength);
1376       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 67);
1377       result.position(bytesLength - 1);
1378       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 89);
1379 
1380       // Enable aliasing
1381       inputStream = inputType.newDecoder(data, data.length);
1382       inputStream.enableAliasing(true);
1383       result = inputStream.readByteBuffer();
1384       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(0);
1385       result = inputStream.readByteBuffer();
1386       if (result.hasArray()) {
1387         assertWithMessage(inputType.name()).that(result.array() == data).isTrue();
1388       }
1389       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1390       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 23);
1391       result = inputStream.readByteBuffer();
1392       if (result.hasArray()) {
1393         assertWithMessage(inputType.name()).that(result.array() == data).isTrue();
1394       }
1395       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(1);
1396       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 45);
1397       result = inputStream.readByteBuffer();
1398       if (result.hasArray()) {
1399         assertWithMessage(inputType.name()).that(result.array() == data).isTrue();
1400       }
1401       assertWithMessage(inputType.name()).that(result.capacity()).isEqualTo(bytesLength);
1402       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 67);
1403       result.position(bytesLength - 1);
1404       assertWithMessage(inputType.name()).that(result.get()).isEqualTo((byte) 89);
1405     }
1406   }
1407 
1408   @Test
testIterableByteBufferInputStreamReadBytesWithAlias()1409   public void testIterableByteBufferInputStreamReadBytesWithAlias() throws Exception {
1410     ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
1411     CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream);
1412     // A bytes field large enough that won't fit into the default block buffer.
1413     // 4.5 is to test the case where the total size of input is not aligned with DEFAULT_BLOCK_SIZE.
1414     final int bytesLength = DEFAULT_BLOCK_SIZE * 4 + (DEFAULT_BLOCK_SIZE / 2);
1415     byte[] bytes = new byte[bytesLength];
1416     for (int i = 0; i < bytesLength; i++) {
1417       bytes[i] = (byte) (i % 256);
1418     }
1419     output.writeByteArrayNoTag(bytes);
1420     output.flush();
1421 
1422     // Input data is split into multiple ByteBuffers so that a single bytes spans across them.
1423     // CodedInputStream with aliasing will decode it as a consequent rope by wrapping ByteBuffers.
1424     byte[] data = byteArrayStream.toByteArray();
1425     ArrayList<ByteBuffer> input = new ArrayList<>();
1426     for (int i = 0; i < data.length; i += DEFAULT_BLOCK_SIZE) {
1427       int rl = Math.min(DEFAULT_BLOCK_SIZE, data.length - i);
1428       ByteBuffer rb = ByteBuffer.allocateDirect(rl);
1429       rb.put(data, i, rl);
1430       rb.flip();
1431       input.add(rb);
1432     }
1433     final CodedInputStream inputStream = CodedInputStream.newInstance(input, true);
1434     inputStream.enableAliasing(true);
1435 
1436     ByteString result = inputStream.readBytes();
1437     for (int i = 0; i < bytesLength; i++) {
1438       assertThat(result.byteAt(i)).isEqualTo((byte) (i % 256));
1439     }
1440   }
1441 
1442   @Test
testCompatibleTypes()1443   public void testCompatibleTypes() throws Exception {
1444     long data = 0x100000000L;
1445     Int64Message message = Int64Message.newBuilder().setData(data).build();
1446     byte[] serialized = message.toByteArray();
1447     for (InputType inputType : InputType.values()) {
1448       CodedInputStream inputStream = inputType.newDecoder(serialized);
1449 
1450       // Test int64(long) is compatible with bool(boolean)
1451       BoolMessage msg2 = BoolMessage.parseFrom(inputStream);
1452       assertThat(msg2.getData()).isTrue();
1453 
1454       // Test int64(long) is compatible with int32(int)
1455       inputStream = inputType.newDecoder(serialized);
1456       Int32Message msg3 = Int32Message.parseFrom(inputStream);
1457       assertThat(msg3.getData()).isEqualTo((int) data);
1458     }
1459   }
1460 
1461   @Test
testSkipInvalidVarint_FastPath()1462   public void testSkipInvalidVarint_FastPath() throws Exception {
1463     // Fast path: We have >= 10 bytes available. Ensure we properly recognize a non-ending varint.
1464     byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0};
1465     for (InputType inputType : InputType.values()) {
1466       try {
1467         CodedInputStream input = inputType.newDecoder(data);
1468         input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
1469         assertWithMessage("%s: Should have thrown an exception.", inputType.name()).fail();
1470       } catch (InvalidProtocolBufferException e) {
1471         // Expected
1472       }
1473     }
1474   }
1475 
1476   @Test
testSkipInvalidVarint_SlowPath()1477   public void testSkipInvalidVarint_SlowPath() throws Exception {
1478     // Slow path: < 10 bytes available. Ensure we properly recognize a non-ending varint.
1479     byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1};
1480     for (InputType inputType : InputType.values()) {
1481       try {
1482         CodedInputStream input = inputType.newDecoder(data);
1483         input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
1484         assertWithMessage("%s: Should have thrown an exception.", inputType.name()).fail();
1485       } catch (InvalidProtocolBufferException e) {
1486         // Expected
1487       }
1488     }
1489   }
1490 
1491   @Test
testSkipPastEndOfByteArrayInput()1492   public void testSkipPastEndOfByteArrayInput() throws Exception {
1493     try {
1494       CodedInputStream.newInstance(new ByteArrayInputStream(new byte[100])).skipRawBytes(101);
1495       assertWithMessage("Should have thrown an exception").fail();
1496     } catch (InvalidProtocolBufferException e) {
1497       // Expected
1498     }
1499   }
1500 
1501   @Test
testMaliciousInputStream()1502   public void testMaliciousInputStream() throws Exception {
1503     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1504     CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
1505     codedOutputStream.writeByteArrayNoTag(new byte[] {0x0, 0x1, 0x2, 0x3, 0x4, 0x5});
1506     codedOutputStream.flush();
1507     final List<byte[]> maliciousCapture = new ArrayList<>();
1508     InputStream inputStream =
1509         new ByteArrayInputStream(outputStream.toByteArray()) {
1510           @Override
1511           public synchronized int read(byte[] b, int off, int len) {
1512             maliciousCapture.add(b);
1513             return super.read(b, off, len);
1514           }
1515         };
1516 
1517     // test ByteString
1518 
1519     CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream, 1);
1520     ByteString byteString = codedInputStream.readBytes();
1521     assertThat(byteString.byteAt(0)).isEqualTo(0x0);
1522     maliciousCapture.get(1)[0] = 0x9;
1523     assertThat(byteString.byteAt(0)).isEqualTo(0x0);
1524 
1525     // test ByteBuffer
1526 
1527     inputStream.reset();
1528     maliciousCapture.clear();
1529     codedInputStream = CodedInputStream.newInstance(inputStream, 1);
1530     ByteBuffer byteBuffer = codedInputStream.readByteBuffer();
1531     assertThat(byteBuffer.get(0)).isEqualTo(0x0);
1532     maliciousCapture.get(1)[0] = 0x9;
1533     assertThat(byteBuffer.get(0)).isEqualTo(0x0);
1534 
1535     // test byte[]
1536 
1537     inputStream.reset();
1538     maliciousCapture.clear();
1539     codedInputStream = CodedInputStream.newInstance(inputStream, 1);
1540     byte[] byteArray = codedInputStream.readByteArray();
1541     assertThat(byteArray[0]).isEqualTo(0x0);
1542     maliciousCapture.get(1)[0] = 0x9;
1543     assertThat(byteArray[0]).isEqualTo(0x9); // MODIFICATION! Should we fix?
1544 
1545     // test rawBytes
1546 
1547     inputStream.reset();
1548     maliciousCapture.clear();
1549     codedInputStream = CodedInputStream.newInstance(inputStream, 1);
1550     int length = codedInputStream.readRawVarint32();
1551     byteArray = codedInputStream.readRawBytes(length);
1552     assertThat(byteArray[0]).isEqualTo(0x0);
1553     maliciousCapture.get(1)[0] = 0x9;
1554     assertThat(byteArray[0]).isEqualTo(0x9); // MODIFICATION! Should we fix?
1555   }
1556 
1557   @Test
testInvalidInputYieldsInvalidProtocolBufferException_readTag()1558   public void testInvalidInputYieldsInvalidProtocolBufferException_readTag() throws Exception {
1559     byte[] input = new byte[] {0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x77};
1560     CodedInputStream inputStream = CodedInputStream.newInstance(input);
1561     try {
1562       inputStream.readTag();
1563       int size = inputStream.readRawVarint32();
1564       inputStream.pushLimit(size);
1565       inputStream.readTag();
1566       assertWithMessage("Should have thrown an exception").fail();
1567     } catch (InvalidProtocolBufferException ex) {
1568       // Expected.
1569     }
1570   }
1571 
1572   @Test
testInvalidInputYieldsInvalidProtocolBufferException_readBytes()1573   public void testInvalidInputYieldsInvalidProtocolBufferException_readBytes() throws Exception {
1574     byte[] input =
1575         new byte[] {0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x67, 0x1a, 0x1a};
1576     CodedInputStream inputStream = CodedInputStream.newInstance(input);
1577     try {
1578       inputStream.readTag();
1579       int size = inputStream.readRawVarint32();
1580       inputStream.pushLimit(size);
1581       inputStream.readBytes();
1582       assertWithMessage("Should have thrown an exception").fail();
1583     } catch (InvalidProtocolBufferException ex) {
1584       // Expected.
1585     }
1586   }
1587 }
1588