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