1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import static org.junit.Assert.assertArrayEquals; 34 import protobuf_unittest.UnittestProto.BoolMessage; 35 import protobuf_unittest.UnittestProto.Int32Message; 36 import protobuf_unittest.UnittestProto.Int64Message; 37 import protobuf_unittest.UnittestProto.TestAllTypes; 38 import protobuf_unittest.UnittestProto.TestRecursiveMessage; 39 import java.io.ByteArrayInputStream; 40 import java.io.ByteArrayOutputStream; 41 import java.io.FilterInputStream; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.nio.ByteBuffer; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.List; 48 import junit.framework.TestCase; 49 50 /** 51 * Unit test for {@link CodedInputStream}. 52 * 53 * @author kenton@google.com Kenton Varda 54 */ 55 public class CodedInputStreamTest extends TestCase { 56 57 private static final int DEFAULT_BLOCK_SIZE = 4096; 58 59 private enum InputType { 60 ARRAY { 61 @Override newDecoder(byte[] data, int blockSize)62 CodedInputStream newDecoder(byte[] data, int blockSize) { 63 return CodedInputStream.newInstance(data); 64 } 65 }, 66 NIO_HEAP { 67 @Override newDecoder(byte[] data, int blockSize)68 CodedInputStream newDecoder(byte[] data, int blockSize) { 69 return CodedInputStream.newInstance(ByteBuffer.wrap(data)); 70 } 71 }, 72 NIO_DIRECT { 73 @Override newDecoder(byte[] data, int blockSize)74 CodedInputStream newDecoder(byte[] data, int blockSize) { 75 ByteBuffer buffer = ByteBuffer.allocateDirect(data.length); 76 buffer.put(data); 77 buffer.flip(); 78 return CodedInputStream.newInstance(buffer); 79 } 80 }, 81 STREAM { 82 @Override newDecoder(byte[] data, int blockSize)83 CodedInputStream newDecoder(byte[] data, int blockSize) { 84 return CodedInputStream.newInstance(new SmallBlockInputStream(data, blockSize)); 85 } 86 }, 87 ITER_DIRECT { 88 @Override newDecoder(byte[] data, int blockSize)89 CodedInputStream newDecoder(byte[] data, int blockSize) { 90 if (blockSize > DEFAULT_BLOCK_SIZE) { 91 blockSize = DEFAULT_BLOCK_SIZE; 92 } 93 ArrayList<ByteBuffer> input = new ArrayList<ByteBuffer>(); 94 for (int i = 0; i < data.length; i += blockSize) { 95 int rl = Math.min(blockSize, data.length - i); 96 ByteBuffer rb = ByteBuffer.allocateDirect(rl); 97 rb.put(data, i, rl); 98 rb.flip(); 99 input.add(rb); 100 } 101 return CodedInputStream.newInstance(input); 102 } 103 }, 104 STREAM_ITER_DIRECT { 105 @Override newDecoder(byte[] data, int blockSize)106 CodedInputStream newDecoder(byte[] data, int blockSize) { 107 if (blockSize > DEFAULT_BLOCK_SIZE) { 108 blockSize = DEFAULT_BLOCK_SIZE; 109 } 110 ArrayList<ByteBuffer> input = new ArrayList<ByteBuffer>(); 111 for (int i = 0; i < data.length; i += blockSize) { 112 int rl = Math.min(blockSize, data.length - i); 113 ByteBuffer rb = ByteBuffer.allocateDirect(rl); 114 rb.put(data, i, rl); 115 rb.flip(); 116 input.add(rb); 117 } 118 return CodedInputStream.newInstance(new IterableByteBufferInputStream(input)); 119 } 120 }; 121 newDecoder(byte[] data)122 CodedInputStream newDecoder(byte[] data) { 123 return newDecoder(data, data.length); 124 } 125 newDecoder(byte[] data, int blockSize)126 abstract CodedInputStream newDecoder(byte[] data, int blockSize); 127 } 128 129 /** 130 * Helper to construct a byte array from a bunch of bytes. The inputs are actually ints so that I 131 * can use hex notation and not get stupid errors about precision. 132 */ bytes(int... bytesAsInts)133 private byte[] bytes(int... bytesAsInts) { 134 byte[] bytes = new byte[bytesAsInts.length]; 135 for (int i = 0; i < bytesAsInts.length; i++) { 136 bytes[i] = (byte) bytesAsInts[i]; 137 } 138 return bytes; 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 assertEquals(msg, data.length, input.getTotalBytesRead()); 183 assertTrue(msg, input.isAtEnd()); 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 assertEquals(inputType.name(), (int) value, input.readRawVarint32()); 196 assertDataConsumed(inputType.name(), data, input); 197 198 input = inputType.newDecoder(data, blockSize); 199 assertEquals(inputType.name(), value, input.readRawVarint64()); 200 assertDataConsumed(inputType.name(), data, input); 201 202 input = inputType.newDecoder(data, blockSize); 203 assertEquals(inputType.name(), value, input.readRawVarint64SlowPath()); 204 assertDataConsumed(inputType.name(), data, input); 205 206 input = inputType.newDecoder(data, blockSize); 207 assertTrue(inputType.name(), input.skipField(WireFormat.WIRETYPE_VARINT)); 208 assertDataConsumed(inputType.name(), data, input); 209 } 210 } 211 212 // Try reading direct from an InputStream. We want to verify that it 213 // doesn't read past the end of the input, so we copy to a new, bigger 214 // array first. 215 byte[] longerData = new byte[data.length + 1]; 216 System.arraycopy(data, 0, longerData, 0, data.length); 217 InputStream rawInput = new ByteArrayInputStream(longerData); 218 assertEquals((int) value, CodedInputStream.readRawVarint32(rawInput)); 219 assertEquals(1, rawInput.available()); 220 } 221 222 /** 223 * Parses the given bytes using readRawVarint32() and readRawVarint64() and expects them to fail 224 * with an InvalidProtocolBufferException whose description matches the given one. 225 */ assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)226 private void assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data) 227 throws Exception { 228 for (InputType inputType : InputType.values()) { 229 try { 230 CodedInputStream input = inputType.newDecoder(data); 231 input.readRawVarint32(); 232 fail(inputType.name() + ": Should have thrown an exception."); 233 } catch (InvalidProtocolBufferException e) { 234 assertEquals(inputType.name(), expected.getMessage(), e.getMessage()); 235 } 236 try { 237 CodedInputStream input = inputType.newDecoder(data); 238 input.readRawVarint64(); 239 fail(inputType.name() + ": Should have thrown an exception."); 240 } catch (InvalidProtocolBufferException e) { 241 assertEquals(inputType.name(), expected.getMessage(), e.getMessage()); 242 } 243 } 244 245 // Make sure we get the same error when reading direct from an InputStream. 246 try { 247 CodedInputStream.readRawVarint32(new ByteArrayInputStream(data)); 248 fail("Should have thrown an exception."); 249 } catch (InvalidProtocolBufferException e) { 250 assertEquals(expected.getMessage(), e.getMessage()); 251 } 252 } 253 254 /** Tests readRawVarint32() and readRawVarint64(). */ testReadVarint()255 public void testReadVarint() throws Exception { 256 assertReadVarint(bytes(0x00), 0); 257 assertReadVarint(bytes(0x01), 1); 258 assertReadVarint(bytes(0x7f), 127); 259 // 14882 260 assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); 261 // 2961488830 262 assertReadVarint( 263 bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), 264 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28)); 265 266 // 64-bit 267 // 7256456126 268 assertReadVarint( 269 bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), 270 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28)); 271 // 41256202580718336 272 assertReadVarint( 273 bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), 274 (0x00 << 0) 275 | (0x66 << 7) 276 | (0x6b << 14) 277 | (0x1c << 21) 278 | (0x43L << 28) 279 | (0x49L << 35) 280 | (0x24L << 42) 281 | (0x49L << 49)); 282 // 11964378330978735131 283 assertReadVarint( 284 bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), 285 (0x1b << 0) 286 | (0x28 << 7) 287 | (0x79 << 14) 288 | (0x42 << 21) 289 | (0x3bL << 28) 290 | (0x56L << 35) 291 | (0x00L << 42) 292 | (0x05L << 49) 293 | (0x26L << 56) 294 | (0x01L << 63)); 295 296 // Failures 297 assertReadVarintFailure( 298 InvalidProtocolBufferException.malformedVarint(), 299 bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00)); 300 assertReadVarintFailure(InvalidProtocolBufferException.truncatedMessage(), bytes(0x80)); 301 } 302 303 /** 304 * Parses the given bytes using readRawLittleEndian32() and checks that the result matches the 305 * given value. 306 */ assertReadLittleEndian32(byte[] data, int value)307 private void assertReadLittleEndian32(byte[] data, int value) throws Exception { 308 for (InputType inputType : InputType.values()) { 309 // Try different block sizes. 310 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { 311 CodedInputStream input = inputType.newDecoder(data, blockSize); 312 assertEquals(inputType.name(), value, input.readRawLittleEndian32()); 313 assertTrue(inputType.name(), input.isAtEnd()); 314 } 315 } 316 } 317 318 /** 319 * Parses the given bytes using readRawLittleEndian64() and checks that the result matches the 320 * given value. 321 */ assertReadLittleEndian64(byte[] data, long value)322 private void assertReadLittleEndian64(byte[] data, long value) throws Exception { 323 for (InputType inputType : InputType.values()) { 324 // Try different block sizes. 325 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { 326 CodedInputStream input = inputType.newDecoder(data, blockSize); 327 assertEquals(inputType.name(), value, input.readRawLittleEndian64()); 328 assertTrue(inputType.name(), input.isAtEnd()); 329 } 330 } 331 } 332 333 /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */ testReadLittleEndian()334 public void testReadLittleEndian() throws Exception { 335 assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); 336 assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); 337 338 assertReadLittleEndian64( 339 bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L); 340 assertReadLittleEndian64( 341 bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L); 342 } 343 344 /** Test decodeZigZag32() and decodeZigZag64(). */ testDecodeZigZag()345 public void testDecodeZigZag() throws Exception { 346 assertEquals(0, CodedInputStream.decodeZigZag32(0)); 347 assertEquals(-1, CodedInputStream.decodeZigZag32(1)); 348 assertEquals(1, CodedInputStream.decodeZigZag32(2)); 349 assertEquals(-2, CodedInputStream.decodeZigZag32(3)); 350 assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE)); 351 assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF)); 352 assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE)); 353 assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF)); 354 355 assertEquals(0, CodedInputStream.decodeZigZag64(0)); 356 assertEquals(-1, CodedInputStream.decodeZigZag64(1)); 357 assertEquals(1, CodedInputStream.decodeZigZag64(2)); 358 assertEquals(-2, CodedInputStream.decodeZigZag64(3)); 359 assertEquals(0x000000003FFFFFFFL, CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL)); 360 assertEquals(0xFFFFFFFFC0000000L, CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL)); 361 assertEquals(0x000000007FFFFFFFL, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL)); 362 assertEquals(0xFFFFFFFF80000000L, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL)); 363 assertEquals(0x7FFFFFFFFFFFFFFFL, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL)); 364 assertEquals(0x8000000000000000L, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL)); 365 } 366 367 /** Tests reading and parsing a whole message with every field type. */ testReadWholeMessage()368 public void testReadWholeMessage() throws Exception { 369 TestAllTypes message = TestUtil.getAllSet(); 370 371 byte[] rawBytes = message.toByteArray(); 372 assertEquals(rawBytes.length, message.getSerializedSize()); 373 374 for (InputType inputType : InputType.values()) { 375 // Try different block sizes. 376 for (int blockSize = 1; blockSize < 256; blockSize *= 2) { 377 TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(rawBytes, blockSize)); 378 TestUtil.assertAllFieldsSet(message2); 379 } 380 } 381 } 382 383 /** Tests skipField(). */ testSkipWholeMessage()384 public void testSkipWholeMessage() throws Exception { 385 TestAllTypes message = TestUtil.getAllSet(); 386 byte[] rawBytes = message.toByteArray(); 387 388 InputType[] inputTypes = InputType.values(); 389 CodedInputStream[] inputs = new CodedInputStream[inputTypes.length]; 390 for (int i = 0; i < inputs.length; ++i) { 391 inputs[i] = inputTypes[i].newDecoder(rawBytes); 392 } 393 UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder(); 394 395 while (true) { 396 CodedInputStream input1 = inputs[0]; 397 int tag = input1.readTag(); 398 // Ensure that the rest match. 399 for (int i = 1; i < inputs.length; ++i) { 400 assertEquals(inputTypes[i].name(), tag, inputs[i].readTag()); 401 } 402 if (tag == 0) { 403 break; 404 } 405 unknownFields.mergeFieldFrom(tag, input1); 406 // Skip the field for the rest of the inputs. 407 for (int i = 1; i < inputs.length; ++i) { 408 inputs[i].skipField(tag); 409 } 410 } 411 } 412 413 414 /** 415 * Test that a bug in skipRawBytes() has been fixed: if the skip skips exactly up to a limit, this 416 * should not break things. 417 */ testSkipRawBytesBug()418 public void testSkipRawBytesBug() throws Exception { 419 byte[] rawBytes = new byte[] {1, 2}; 420 for (InputType inputType : InputType.values()) { 421 CodedInputStream input = inputType.newDecoder(rawBytes); 422 int limit = input.pushLimit(1); 423 input.skipRawBytes(1); 424 input.popLimit(limit); 425 assertEquals(inputType.name(), 2, input.readRawByte()); 426 } 427 } 428 429 /** 430 * Test that a bug in skipRawBytes() has been fixed: if the skip skips past the end of a buffer 431 * with a limit that has been set past the end of that buffer, this should not break things. 432 */ testSkipRawBytesPastEndOfBufferWithLimit()433 public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception { 434 byte[] rawBytes = new byte[] {1, 2, 3, 4, 5}; 435 for (InputType inputType : InputType.values()) { 436 CodedInputStream input = inputType.newDecoder(rawBytes); 437 int limit = input.pushLimit(4); 438 // In order to expose the bug we need to read at least one byte to prime the 439 // buffer inside the CodedInputStream. 440 assertEquals(inputType.name(), 1, input.readRawByte()); 441 // Skip to the end of the limit. 442 input.skipRawBytes(3); 443 assertTrue(inputType.name(), input.isAtEnd()); 444 input.popLimit(limit); 445 assertEquals(inputType.name(), 5, input.readRawByte()); 446 } 447 } 448 449 /** 450 * Test that calling skipRawBytes (when not merging a message) actually skips from the underlying 451 * inputstream, regardless of the buffer size used. 452 */ testSkipRawBytesActuallySkips()453 public void testSkipRawBytesActuallySkips() throws Exception { 454 SmallBlockInputStream bytes = new SmallBlockInputStream(new byte[] {1, 2, 3, 4, 5}, 3); 455 CodedInputStream input = CodedInputStream.newInstance(bytes, 1); // Tiny buffer 456 input.skipRawBytes(3); 457 input.skipRawBytes(2); 458 assertEquals(2, bytes.skipCalls); 459 assertEquals(0, bytes.readCalls); 460 } 461 testSkipHugeBlob()462 public void testSkipHugeBlob() throws Exception { 463 // Allocate and initialize a 1MB blob. 464 int blobSize = 1 << 20; 465 byte[] blob = new byte[blobSize]; 466 for (int i = 0; i < blob.length; i++) { 467 blob[i] = (byte) i; 468 } 469 470 for (InputType inputType : InputType.values()) { 471 CodedInputStream decoder = inputType.newDecoder(blob); 472 decoder.skipRawBytes(blobSize - 10); 473 byte[] remaining = decoder.readRawBytes(10); 474 assertArrayEquals(Arrays.copyOfRange(blob, blobSize - 10, blobSize), remaining); 475 } 476 } 477 478 /** Skipping a huge blob should not allocate excessive memory, so there should be no limit */ testSkipMaliciouslyHugeBlob()479 public void testSkipMaliciouslyHugeBlob() throws Exception { 480 InputStream is = new RepeatingInputStream(new byte[]{1}, Integer.MAX_VALUE); 481 CodedInputStream.newInstance(is).skipRawBytes(Integer.MAX_VALUE); 482 } 483 testReadHugeBlob()484 public void testReadHugeBlob() throws Exception { 485 // Allocate and initialize a 1MB blob. 486 byte[] blob = new byte[1 << 20]; 487 for (int i = 0; i < blob.length; i++) { 488 blob[i] = (byte) i; 489 } 490 491 // Make a message containing it. 492 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 493 TestUtil.setAllFields(builder); 494 builder.setOptionalBytes(ByteString.copyFrom(blob)); 495 TestAllTypes message = builder.build(); 496 497 byte[] data = message.toByteArray(); 498 for (InputType inputType : InputType.values()) { 499 // Serialize and parse it. Make sure to parse from an InputStream, not 500 // directly from a ByteString, so that CodedInputStream uses buffered 501 // reading. 502 TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(data)); 503 504 assertEquals(inputType.name(), message.getOptionalBytes(), message2.getOptionalBytes()); 505 506 // Make sure all the other fields were parsed correctly. 507 TestAllTypes message3 = 508 TestAllTypes.newBuilder(message2) 509 .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes()) 510 .build(); 511 TestUtil.assertAllFieldsSet(message3); 512 } 513 } 514 testReadMaliciouslyLargeBlob()515 public void testReadMaliciouslyLargeBlob() throws Exception { 516 ByteString.Output rawOutput = ByteString.newOutput(); 517 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 518 519 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 520 output.writeRawVarint32(tag); 521 output.writeRawVarint32(0x7FFFFFFF); 522 output.writeRawBytes(new byte[32]); // Pad with a few random bytes. 523 output.flush(); 524 525 byte[] data = rawOutput.toByteString().toByteArray(); 526 for (InputType inputType : InputType.values()) { 527 CodedInputStream input = inputType.newDecoder(data); 528 assertEquals(tag, input.readTag()); 529 try { 530 input.readBytes(); 531 fail(inputType.name() + ": Should have thrown an exception!"); 532 } catch (InvalidProtocolBufferException e) { 533 // success. 534 } 535 } 536 } 537 538 /** 539 * Test we can do messages that are up to CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G or 540 * Integer#MAX_SIZE). 541 * 542 * @throws IOException 543 */ testParseMessagesCloseTo2G()544 public void testParseMessagesCloseTo2G() throws IOException { 545 byte[] serializedMessage = getBigSerializedMessage(); 546 // How many of these big messages do we need to take us near our 2G limit? 547 int count = Integer.MAX_VALUE / serializedMessage.length; 548 // Now make an inputstream that will fake a near 2G message of messages 549 // returning our big serialized message 'count' times. 550 InputStream is = new RepeatingInputStream(serializedMessage, count); 551 // Parse should succeed! 552 TestAllTypes.parseFrom(is); 553 } 554 555 /** 556 * Test there is an exception if a message exceeds CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G 557 * or Integer#MAX_SIZE). 558 * 559 * @throws IOException 560 */ testParseMessagesOver2G()561 public void testParseMessagesOver2G() throws IOException { 562 byte[] serializedMessage = getBigSerializedMessage(); 563 // How many of these big messages do we need to take us near our 2G limit? 564 int count = Integer.MAX_VALUE / serializedMessage.length; 565 // Now add one to take us over the limit 566 count++; 567 // Now make an inputstream that will fake a near 2G message of messages 568 // returning our big serialized message 'count' times. 569 InputStream is = new RepeatingInputStream(serializedMessage, count); 570 try { 571 TestAllTypes.parseFrom(is); 572 fail("Should have thrown an exception!"); 573 } catch (InvalidProtocolBufferException e) { 574 assertTrue(e.getMessage().contains("too large")); 575 } 576 } 577 578 /* 579 * @return A serialized big message. 580 */ getBigSerializedMessage()581 private static byte[] getBigSerializedMessage() { 582 byte[] value = new byte[16 * 1024 * 1024]; 583 ByteString bsValue = ByteString.wrap(value); 584 return TestAllTypes.newBuilder().setOptionalBytes(bsValue).build().toByteArray(); 585 } 586 587 /* 588 * An input stream that repeats a byte arrays' content a number of times. 589 * Simulates really large input without consuming loads of memory. Used above 590 * to test the parsing behavior when the input size exceeds 2G or close to it. 591 */ 592 private static class RepeatingInputStream extends InputStream { 593 private final byte[] serializedMessage; 594 private final int count; 595 private int index = 0; 596 private int offset = 0; 597 RepeatingInputStream(byte[] serializedMessage, int count)598 RepeatingInputStream(byte[] serializedMessage, int count) { 599 this.serializedMessage = serializedMessage; 600 this.count = count; 601 } 602 603 @Override read()604 public int read() throws IOException { 605 if (this.offset == this.serializedMessage.length) { 606 this.index++; 607 this.offset = 0; 608 } 609 if (this.index == this.count) { 610 return -1; 611 } 612 return this.serializedMessage[offset++]; 613 } 614 } 615 makeRecursiveMessage(int depth)616 private TestRecursiveMessage makeRecursiveMessage(int depth) { 617 if (depth == 0) { 618 return TestRecursiveMessage.newBuilder().setI(5).build(); 619 } else { 620 return TestRecursiveMessage.newBuilder().setA(makeRecursiveMessage(depth - 1)).build(); 621 } 622 } 623 assertMessageDepth(String msg, TestRecursiveMessage message, int depth)624 private void assertMessageDepth(String msg, TestRecursiveMessage message, int depth) { 625 if (depth == 0) { 626 assertFalse(msg, message.hasA()); 627 assertEquals(msg, 5, message.getI()); 628 } else { 629 assertTrue(msg, message.hasA()); 630 assertMessageDepth(msg, message.getA(), depth - 1); 631 } 632 } 633 testMaliciousRecursion()634 public void testMaliciousRecursion() throws Exception { 635 byte[] data100 = makeRecursiveMessage(100).toByteArray(); 636 byte[] data101 = makeRecursiveMessage(101).toByteArray(); 637 638 for (InputType inputType : InputType.values()) { 639 assertMessageDepth( 640 inputType.name(), TestRecursiveMessage.parseFrom(inputType.newDecoder(data100)), 100); 641 642 try { 643 TestRecursiveMessage.parseFrom(inputType.newDecoder(data101)); 644 fail("Should have thrown an exception!"); 645 } catch (InvalidProtocolBufferException e) { 646 // success. 647 } 648 649 CodedInputStream input = inputType.newDecoder(data100); 650 input.setRecursionLimit(8); 651 try { 652 TestRecursiveMessage.parseFrom(input); 653 fail(inputType.name() + ": Should have thrown an exception!"); 654 } catch (InvalidProtocolBufferException e) { 655 // success. 656 } 657 } 658 } 659 checkSizeLimitExceeded(InvalidProtocolBufferException e)660 private void checkSizeLimitExceeded(InvalidProtocolBufferException e) { 661 assertEquals(InvalidProtocolBufferException.sizeLimitExceeded().getMessage(), e.getMessage()); 662 } 663 testSizeLimit()664 public void testSizeLimit() throws Exception { 665 // NOTE: Size limit only applies to the stream-backed CIS. 666 CodedInputStream input = 667 CodedInputStream.newInstance( 668 new SmallBlockInputStream(TestUtil.getAllSet().toByteArray(), 16)); 669 input.setSizeLimit(16); 670 671 try { 672 TestAllTypes.parseFrom(input); 673 fail("Should have thrown an exception!"); 674 } catch (InvalidProtocolBufferException expected) { 675 checkSizeLimitExceeded(expected); 676 } 677 } 678 testResetSizeCounter()679 public void testResetSizeCounter() throws Exception { 680 // NOTE: Size limit only applies to the stream-backed CIS. 681 CodedInputStream input = 682 CodedInputStream.newInstance(new SmallBlockInputStream(new byte[256], 8)); 683 input.setSizeLimit(16); 684 input.readRawBytes(16); 685 assertEquals(16, input.getTotalBytesRead()); 686 687 try { 688 input.readRawByte(); 689 fail("Should have thrown an exception!"); 690 } catch (InvalidProtocolBufferException expected) { 691 checkSizeLimitExceeded(expected); 692 } 693 694 input.resetSizeCounter(); 695 assertEquals(0, input.getTotalBytesRead()); 696 input.readRawByte(); // No exception thrown. 697 input.resetSizeCounter(); 698 assertEquals(0, input.getTotalBytesRead()); 699 input.readRawBytes(16); 700 assertEquals(16, input.getTotalBytesRead()); 701 input.resetSizeCounter(); 702 703 try { 704 input.readRawBytes(17); // Hits limit again. 705 fail("Should have thrown an exception!"); 706 } catch (InvalidProtocolBufferException expected) { 707 checkSizeLimitExceeded(expected); 708 } 709 } 710 testRefillBufferWithCorrectSize()711 public void testRefillBufferWithCorrectSize() throws Exception { 712 // NOTE: refillBuffer only applies to the stream-backed CIS. 713 byte[] bytes = "123456789".getBytes("UTF-8"); 714 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 715 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); 716 717 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 718 output.writeRawVarint32(tag); 719 output.writeRawVarint32(bytes.length); 720 output.writeRawBytes(bytes); 721 output.writeRawVarint32(tag); 722 output.writeRawVarint32(bytes.length); 723 output.writeRawBytes(bytes); 724 output.writeRawByte(4); 725 output.flush(); 726 727 // Input is two string with length 9 and one raw byte. 728 byte[] rawInput = rawOutput.toByteArray(); 729 for (int inputStreamBufferLength = 8; 730 inputStreamBufferLength <= rawInput.length + 1; 731 inputStreamBufferLength++) { 732 CodedInputStream input = 733 CodedInputStream.newInstance(new ByteArrayInputStream(rawInput), inputStreamBufferLength); 734 input.setSizeLimit(rawInput.length - 1); 735 input.readString(); 736 input.readString(); 737 try { 738 input.readRawByte(); // Hits limit. 739 fail("Should have thrown an exception!"); 740 } catch (InvalidProtocolBufferException expected) { 741 checkSizeLimitExceeded(expected); 742 } 743 } 744 } 745 testIsAtEnd()746 public void testIsAtEnd() throws Exception { 747 CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(new byte[5])); 748 try { 749 for (int i = 0; i < 5; i++) { 750 assertEquals(false, input.isAtEnd()); 751 input.readRawByte(); 752 } 753 assertEquals(true, input.isAtEnd()); 754 } catch (Exception e) { 755 throw new AssertionError("Catch exception in the testIsAtEnd", e); 756 } 757 } 758 testCurrentLimitExceeded()759 public void testCurrentLimitExceeded() throws Exception { 760 byte[] bytes = "123456789".getBytes("UTF-8"); 761 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 762 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); 763 764 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 765 output.writeRawVarint32(tag); 766 output.writeRawVarint32(bytes.length); 767 output.writeRawBytes(bytes); 768 output.flush(); 769 770 byte[] rawInput = rawOutput.toByteArray(); 771 CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(rawInput)); 772 // The length of the whole rawInput 773 input.setSizeLimit(11); 774 // Some number that is smaller than the rawInput's length 775 // but larger than 2 776 input.pushLimit(5); 777 try { 778 input.readString(); 779 fail("Should have thrown an exception"); 780 } catch (InvalidProtocolBufferException expected) { 781 assertEquals( 782 expected.getMessage(), InvalidProtocolBufferException.truncatedMessage().getMessage()); 783 } 784 } 785 testSizeLimitMultipleMessages()786 public void testSizeLimitMultipleMessages() throws Exception { 787 // NOTE: Size limit only applies to the stream-backed CIS. 788 byte[] bytes = new byte[256]; 789 for (int i = 0; i < bytes.length; i++) { 790 bytes[i] = (byte) i; 791 } 792 CodedInputStream input = CodedInputStream.newInstance(new SmallBlockInputStream(bytes, 7)); 793 input.setSizeLimit(16); 794 for (int i = 0; i < 256 / 16; i++) { 795 byte[] message = input.readRawBytes(16); 796 for (int j = 0; j < message.length; j++) { 797 assertEquals(i * 16 + j, message[j] & 0xff); 798 } 799 assertEquals(16, input.getTotalBytesRead()); 800 input.resetSizeCounter(); 801 assertEquals(0, input.getTotalBytesRead()); 802 } 803 } 804 testReadString()805 public void testReadString() throws Exception { 806 String lorem = "Lorem ipsum dolor sit amet "; 807 StringBuilder builder = new StringBuilder(); 808 for (int i = 0; i < 4096; i += lorem.length()) { 809 builder.append(lorem); 810 } 811 lorem = builder.toString().substring(0, 4096); 812 byte[] bytes = lorem.getBytes("UTF-8"); 813 ByteString.Output rawOutput = ByteString.newOutput(); 814 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); 815 816 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 817 output.writeRawVarint32(tag); 818 output.writeRawVarint32(bytes.length); 819 output.writeRawBytes(bytes); 820 output.flush(); 821 822 byte[] rawInput = rawOutput.toByteString().toByteArray(); 823 for (InputType inputType : InputType.values()) { 824 CodedInputStream input = inputType.newDecoder(rawInput); 825 assertEquals(inputType.name(), tag, input.readTag()); 826 String text = input.readString(); 827 assertEquals(inputType.name(), lorem, text); 828 } 829 } 830 testReadStringRequireUtf8()831 public void testReadStringRequireUtf8() throws Exception { 832 String lorem = "Lorem ipsum dolor sit amet "; 833 StringBuilder builder = new StringBuilder(); 834 for (int i = 0; i < 4096; i += lorem.length()) { 835 builder.append(lorem); 836 } 837 lorem = builder.toString().substring(0, 4096); 838 byte[] bytes = lorem.getBytes("UTF-8"); 839 ByteString.Output rawOutput = ByteString.newOutput(); 840 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); 841 842 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 843 output.writeRawVarint32(tag); 844 output.writeRawVarint32(bytes.length); 845 output.writeRawBytes(bytes); 846 output.flush(); 847 848 byte[] rawInput = rawOutput.toByteString().toByteArray(); 849 for (InputType inputType : InputType.values()) { 850 CodedInputStream input = inputType.newDecoder(rawInput); 851 assertEquals(inputType.name(), tag, input.readTag()); 852 String text = input.readStringRequireUtf8(); 853 assertEquals(inputType.name(), lorem, text); 854 } 855 } 856 857 /** 858 * Tests that if we readString invalid UTF-8 bytes, no exception is thrown. Instead, the invalid 859 * bytes are replaced with the Unicode "replacement character" U+FFFD. 860 */ testReadStringInvalidUtf8()861 public void testReadStringInvalidUtf8() throws Exception { 862 ByteString.Output rawOutput = ByteString.newOutput(); 863 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 864 865 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 866 output.writeRawVarint32(tag); 867 output.writeRawVarint32(1); 868 output.writeRawBytes(new byte[] {(byte) 0x80}); 869 output.flush(); 870 871 byte[] rawInput = rawOutput.toByteString().toByteArray(); 872 for (InputType inputType : InputType.values()) { 873 CodedInputStream input = inputType.newDecoder(rawInput); 874 assertEquals(inputType.name(), tag, input.readTag()); 875 String text = input.readString(); 876 assertEquals(inputType.name(), 0xfffd, text.charAt(0)); 877 } 878 } 879 880 /** 881 * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an InvalidProtocolBufferException 882 * is thrown. 883 */ testReadStringRequireUtf8InvalidUtf8()884 public void testReadStringRequireUtf8InvalidUtf8() throws Exception { 885 ByteString.Output rawOutput = ByteString.newOutput(); 886 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 887 888 int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); 889 output.writeRawVarint32(tag); 890 output.writeRawVarint32(1); 891 output.writeRawBytes(new byte[] {(byte) 0x80}); 892 output.flush(); 893 894 byte[] rawInput = rawOutput.toByteString().toByteArray(); 895 for (InputType inputType : InputType.values()) { 896 CodedInputStream input = inputType.newDecoder(rawInput); 897 assertEquals(tag, input.readTag()); 898 try { 899 input.readStringRequireUtf8(); 900 fail(inputType.name() + ": Expected invalid UTF-8 exception."); 901 } catch (InvalidProtocolBufferException exception) { 902 assertEquals( 903 inputType.name(), "Protocol message had invalid UTF-8.", exception.getMessage()); 904 } 905 } 906 } 907 testReadFromSlice()908 public void testReadFromSlice() throws Exception { 909 byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 910 CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5); 911 assertEquals(0, in.getTotalBytesRead()); 912 for (int i = 3; i < 8; i++) { 913 assertEquals(i, in.readRawByte()); 914 assertEquals(i - 2, in.getTotalBytesRead()); 915 } 916 // eof 917 assertEquals(0, in.readTag()); 918 assertEquals(5, in.getTotalBytesRead()); 919 } 920 testInvalidTag()921 public void testInvalidTag() throws Exception { 922 // Any tag number which corresponds to field number zero is invalid and 923 // should throw InvalidProtocolBufferException. 924 for (InputType inputType : InputType.values()) { 925 for (int i = 0; i < 8; i++) { 926 try { 927 inputType.newDecoder(bytes(i)).readTag(); 928 fail(inputType.name() + ": Should have thrown an exception."); 929 } catch (InvalidProtocolBufferException e) { 930 assertEquals( 931 inputType.name(), 932 InvalidProtocolBufferException.invalidTag().getMessage(), 933 e.getMessage()); 934 } 935 } 936 } 937 } 938 testReadByteArray()939 public void testReadByteArray() throws Exception { 940 ByteString.Output rawOutput = ByteString.newOutput(); 941 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 942 // Zero-sized bytes field. 943 output.writeRawVarint32(0); 944 // One one-byte bytes field 945 output.writeRawVarint32(1); 946 output.writeRawBytes(new byte[] {(byte) 23}); 947 // Another one-byte bytes field 948 output.writeRawVarint32(1); 949 output.writeRawBytes(new byte[] {(byte) 45}); 950 // A bytes field large enough that won't fit into the 4K buffer. 951 final int bytesLength = 16 * 1024; 952 byte[] bytes = new byte[bytesLength]; 953 bytes[0] = (byte) 67; 954 bytes[bytesLength - 1] = (byte) 89; 955 output.writeRawVarint32(bytesLength); 956 output.writeRawBytes(bytes); 957 958 output.flush(); 959 960 byte[] rawInput = rawOutput.toByteString().toByteArray(); 961 for (InputType inputType : InputType.values()) { 962 CodedInputStream inputStream = inputType.newDecoder(rawInput); 963 964 byte[] result = inputStream.readByteArray(); 965 assertEquals(inputType.name(), 0, result.length); 966 result = inputStream.readByteArray(); 967 assertEquals(inputType.name(), 1, result.length); 968 assertEquals(inputType.name(), (byte) 23, result[0]); 969 result = inputStream.readByteArray(); 970 assertEquals(inputType.name(), 1, result.length); 971 assertEquals(inputType.name(), (byte) 45, result[0]); 972 result = inputStream.readByteArray(); 973 assertEquals(inputType.name(), bytesLength, result.length); 974 assertEquals(inputType.name(), (byte) 67, result[0]); 975 assertEquals(inputType.name(), (byte) 89, result[bytesLength - 1]); 976 } 977 } 978 testReadLargeByteStringFromInputStream()979 public void testReadLargeByteStringFromInputStream() throws Exception { 980 byte[] bytes = new byte[1024 * 1024]; 981 for (int i = 0; i < bytes.length; i++) { 982 bytes[i] = (byte) (i & 0xFF); 983 } 984 ByteString.Output rawOutput = ByteString.newOutput(); 985 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 986 output.writeRawVarint32(bytes.length); 987 output.writeRawBytes(bytes); 988 output.flush(); 989 byte[] data = rawOutput.toByteString().toByteArray(); 990 991 CodedInputStream input = 992 CodedInputStream.newInstance( 993 new ByteArrayInputStream(data) { 994 @Override 995 public synchronized int available() { 996 return 0; 997 } 998 }); 999 ByteString result = input.readBytes(); 1000 assertEquals(ByteString.copyFrom(bytes), result); 1001 } 1002 testReadLargeByteArrayFromInputStream()1003 public void testReadLargeByteArrayFromInputStream() throws Exception { 1004 byte[] bytes = new byte[1024 * 1024]; 1005 for (int i = 0; i < bytes.length; i++) { 1006 bytes[i] = (byte) (i & 0xFF); 1007 } 1008 ByteString.Output rawOutput = ByteString.newOutput(); 1009 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 1010 output.writeRawVarint32(bytes.length); 1011 output.writeRawBytes(bytes); 1012 output.flush(); 1013 byte[] data = rawOutput.toByteString().toByteArray(); 1014 1015 CodedInputStream input = 1016 CodedInputStream.newInstance( 1017 new ByteArrayInputStream(data) { 1018 @Override 1019 public synchronized int available() { 1020 return 0; 1021 } 1022 }); 1023 byte[] result = input.readByteArray(); 1024 assertTrue(Arrays.equals(bytes, result)); 1025 } 1026 testReadByteBuffer()1027 public void testReadByteBuffer() throws Exception { 1028 ByteString.Output rawOutput = ByteString.newOutput(); 1029 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 1030 // Zero-sized bytes field. 1031 output.writeRawVarint32(0); 1032 // One one-byte bytes field 1033 output.writeRawVarint32(1); 1034 output.writeRawBytes(new byte[] {(byte) 23}); 1035 // Another one-byte bytes field 1036 output.writeRawVarint32(1); 1037 output.writeRawBytes(new byte[] {(byte) 45}); 1038 // A bytes field large enough that won't fit into the 4K buffer. 1039 final int bytesLength = 16 * 1024; 1040 byte[] bytes = new byte[bytesLength]; 1041 bytes[0] = (byte) 67; 1042 bytes[bytesLength - 1] = (byte) 89; 1043 output.writeRawVarint32(bytesLength); 1044 output.writeRawBytes(bytes); 1045 1046 output.flush(); 1047 1048 byte[] rawInput = rawOutput.toByteString().toByteArray(); 1049 for (InputType inputType : InputType.values()) { 1050 CodedInputStream inputStream = inputType.newDecoder(rawInput); 1051 1052 ByteBuffer result = inputStream.readByteBuffer(); 1053 assertEquals(inputType.name(), 0, result.capacity()); 1054 result = inputStream.readByteBuffer(); 1055 assertEquals(inputType.name(), 1, result.capacity()); 1056 assertEquals(inputType.name(), (byte) 23, result.get()); 1057 result = inputStream.readByteBuffer(); 1058 assertEquals(inputType.name(), 1, result.capacity()); 1059 assertEquals(inputType.name(), (byte) 45, result.get()); 1060 result = inputStream.readByteBuffer(); 1061 assertEquals(inputType.name(), bytesLength, result.capacity()); 1062 assertEquals(inputType.name(), (byte) 67, result.get()); 1063 result.position(bytesLength - 1); 1064 assertEquals(inputType.name(), (byte) 89, result.get()); 1065 } 1066 } 1067 testReadByteBufferAliasing()1068 public void testReadByteBufferAliasing() throws Exception { 1069 ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); 1070 CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream); 1071 // Zero-sized bytes field. 1072 output.writeRawVarint32(0); 1073 // One one-byte bytes field 1074 output.writeRawVarint32(1); 1075 output.writeRawBytes(new byte[] {(byte) 23}); 1076 // Another one-byte bytes field 1077 output.writeRawVarint32(1); 1078 output.writeRawBytes(new byte[] {(byte) 45}); 1079 // A bytes field large enough that won't fit into the 4K buffer. 1080 final int bytesLength = 16 * 1024; 1081 byte[] bytes = new byte[bytesLength]; 1082 bytes[0] = (byte) 67; 1083 bytes[bytesLength - 1] = (byte) 89; 1084 output.writeRawVarint32(bytesLength); 1085 output.writeRawBytes(bytes); 1086 output.flush(); 1087 1088 byte[] data = byteArrayStream.toByteArray(); 1089 1090 for (InputType inputType : InputType.values()) { 1091 if (inputType == InputType.STREAM 1092 || inputType == InputType.STREAM_ITER_DIRECT 1093 || inputType == InputType.ITER_DIRECT) { 1094 // Aliasing doesn't apply to stream-backed CIS. 1095 continue; 1096 } 1097 1098 // Without aliasing 1099 CodedInputStream inputStream = inputType.newDecoder(data); 1100 ByteBuffer result = inputStream.readByteBuffer(); 1101 assertEquals(inputType.name(), 0, result.capacity()); 1102 result = inputStream.readByteBuffer(); 1103 assertTrue(inputType.name(), result.array() != data); 1104 assertEquals(inputType.name(), 1, result.capacity()); 1105 assertEquals(inputType.name(), (byte) 23, result.get()); 1106 result = inputStream.readByteBuffer(); 1107 assertTrue(inputType.name(), result.array() != data); 1108 assertEquals(inputType.name(), 1, result.capacity()); 1109 assertEquals(inputType.name(), (byte) 45, result.get()); 1110 result = inputStream.readByteBuffer(); 1111 assertTrue(inputType.name(), result.array() != data); 1112 assertEquals(inputType.name(), bytesLength, result.capacity()); 1113 assertEquals(inputType.name(), (byte) 67, result.get()); 1114 result.position(bytesLength - 1); 1115 assertEquals(inputType.name(), (byte) 89, result.get()); 1116 1117 // Enable aliasing 1118 inputStream = inputType.newDecoder(data, data.length); 1119 inputStream.enableAliasing(true); 1120 result = inputStream.readByteBuffer(); 1121 assertEquals(inputType.name(), 0, result.capacity()); 1122 result = inputStream.readByteBuffer(); 1123 if (result.hasArray()) { 1124 assertTrue(inputType.name(), result.array() == data); 1125 } 1126 assertEquals(inputType.name(), 1, result.capacity()); 1127 assertEquals(inputType.name(), (byte) 23, result.get()); 1128 result = inputStream.readByteBuffer(); 1129 if (result.hasArray()) { 1130 assertTrue(inputType.name(), result.array() == data); 1131 } 1132 assertEquals(inputType.name(), 1, result.capacity()); 1133 assertEquals(inputType.name(), (byte) 45, result.get()); 1134 result = inputStream.readByteBuffer(); 1135 if (result.hasArray()) { 1136 assertTrue(inputType.name(), result.array() == data); 1137 } 1138 assertEquals(inputType.name(), bytesLength, result.capacity()); 1139 assertEquals(inputType.name(), (byte) 67, result.get()); 1140 result.position(bytesLength - 1); 1141 assertEquals(inputType.name(), (byte) 89, result.get()); 1142 } 1143 } 1144 testCompatibleTypes()1145 public void testCompatibleTypes() throws Exception { 1146 long data = 0x100000000L; 1147 Int64Message message = Int64Message.newBuilder().setData(data).build(); 1148 byte[] serialized = message.toByteArray(); 1149 for (InputType inputType : InputType.values()) { 1150 CodedInputStream inputStream = inputType.newDecoder(serialized); 1151 1152 // Test int64(long) is compatible with bool(boolean) 1153 BoolMessage msg2 = BoolMessage.parseFrom(inputStream); 1154 assertTrue(msg2.getData()); 1155 1156 // Test int64(long) is compatible with int32(int) 1157 inputStream = inputType.newDecoder(serialized); 1158 Int32Message msg3 = Int32Message.parseFrom(inputStream); 1159 assertEquals((int) data, msg3.getData()); 1160 } 1161 } 1162 testSkipInvalidVarint_FastPath()1163 public void testSkipInvalidVarint_FastPath() throws Exception { 1164 // Fast path: We have >= 10 bytes available. Ensure we properly recognize a non-ending varint. 1165 byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}; 1166 for (InputType inputType : InputType.values()) { 1167 try { 1168 CodedInputStream input = inputType.newDecoder(data); 1169 input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT)); 1170 fail(inputType.name() + ": Should have thrown an exception."); 1171 } catch (InvalidProtocolBufferException e) { 1172 // Expected 1173 } 1174 } 1175 } 1176 testSkipInvalidVarint_SlowPath()1177 public void testSkipInvalidVarint_SlowPath() throws Exception { 1178 // Slow path: < 10 bytes available. Ensure we properly recognize a non-ending varint. 1179 byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1}; 1180 for (InputType inputType : InputType.values()) { 1181 try { 1182 CodedInputStream input = inputType.newDecoder(data); 1183 input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT)); 1184 fail(inputType.name() + ": Should have thrown an exception."); 1185 } catch (InvalidProtocolBufferException e) { 1186 // Expected 1187 } 1188 } 1189 } 1190 testSkipPastEndOfByteArrayInput()1191 public void testSkipPastEndOfByteArrayInput() throws Exception { 1192 try { 1193 CodedInputStream.newInstance(new ByteArrayInputStream(new byte[100])).skipRawBytes(101); 1194 fail(); 1195 } catch (InvalidProtocolBufferException e) { 1196 // Expected 1197 } 1198 } 1199 testMaliciousInputStream()1200 public void testMaliciousInputStream() throws Exception { 1201 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 1202 CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); 1203 codedOutputStream.writeByteArrayNoTag(new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5 }); 1204 codedOutputStream.flush(); 1205 final List<byte[]> maliciousCapture = new ArrayList<>(); 1206 InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()) { 1207 @Override 1208 public synchronized int read(byte[] b, int off, int len) { 1209 maliciousCapture.add(b); 1210 return super.read(b, off, len); 1211 } 1212 }; 1213 1214 // test ByteString 1215 1216 CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream, 1); 1217 ByteString byteString = codedInputStream.readBytes(); 1218 assertEquals(0x0, byteString.byteAt(0)); 1219 maliciousCapture.get(1)[0] = 0x9; 1220 assertEquals(0x0, byteString.byteAt(0)); 1221 1222 // test ByteBuffer 1223 1224 inputStream.reset(); 1225 maliciousCapture.clear(); 1226 codedInputStream = CodedInputStream.newInstance(inputStream, 1); 1227 ByteBuffer byteBuffer = codedInputStream.readByteBuffer(); 1228 assertEquals(0x0, byteBuffer.get(0)); 1229 maliciousCapture.get(1)[0] = 0x9; 1230 assertEquals(0x0, byteBuffer.get(0)); 1231 1232 1233 // test byte[] 1234 1235 inputStream.reset(); 1236 maliciousCapture.clear(); 1237 codedInputStream = CodedInputStream.newInstance(inputStream, 1); 1238 byte[] byteArray = codedInputStream.readByteArray(); 1239 assertEquals(0x0, byteArray[0]); 1240 maliciousCapture.get(1)[0] = 0x9; 1241 assertEquals(0x9, byteArray[0]); // MODIFICATION! Should we fix? 1242 1243 // test rawBytes 1244 1245 inputStream.reset(); 1246 maliciousCapture.clear(); 1247 codedInputStream = CodedInputStream.newInstance(inputStream, 1); 1248 int length = codedInputStream.readRawVarint32(); 1249 byteArray = codedInputStream.readRawBytes(length); 1250 assertEquals(0x0, byteArray[0]); 1251 maliciousCapture.get(1)[0] = 0x9; 1252 assertEquals(0x9, byteArray[0]); // MODIFICATION! Should we fix? 1253 } 1254 } 1255