From b0fd4a15a7cae946dd2855122559ca59cc34dbea Mon Sep 17 00:00:00 2001 From: Amos Ortal Date: Mon, 6 Jul 2020 15:42:35 -0700 Subject: [PATCH] [c#] Fix handling of large container lengths Fix handling of large container lengths that could cause an infinite loop when deserializing some payloads. This fix addresses [CVE-2020-1469](https://msrc-portal-preview.azurewebsites.net/en-US/security-guidance/advisory/CVE-2020-1469). --- CHANGELOG.md | 3 + cs/src/core/io/safe/InputBuffer.cs | 2 +- cs/src/core/io/safe/OutputBuffer.cs | 4 +- cs/src/core/protocols/CompactBinary.cs | 26 +- cs/src/core/protocols/CompactBinaryCounter.cs | 6 +- cs/src/core/protocols/FastBinary.cs | 27 +- cs/src/core/protocols/SimpleBinary.cs | 8 +- cs/src/io/unsafe/InputPointer.cs | 2 +- cs/test/core/IndexOverflowTests.cs | 647 ++++++++++++++++++ 9 files changed, 690 insertions(+), 35 deletions(-) create mode 100644 cs/test/core/IndexOverflowTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f8ffbb78d..afde4aed62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ different versioning scheme, following the Haskell community's * Fixed MSB3105/CS2002 error about duplicate Compile items when a directory contains multiple .bond files and `--gprc` is in `$BondOptions`. ([Issue #1050](https://github.com/microsoft/bond/issues/1050)) +* Fix handling of large container lengths that could cause an infinite loop + when deserializing some payloads. This fix addresses + [CVE-2020-1469](https://msrc-portal-preview.azurewebsites.net/en-US/security-guidance/advisory/CVE-2020-1469). ## 9.0: 2020-05-26 ## * IDL core version: 3.0 diff --git a/cs/src/core/io/safe/InputBuffer.cs b/cs/src/core/io/safe/InputBuffer.cs index ae55d6b0d6..264dcbc90f 100644 --- a/cs/src/core/io/safe/InputBuffer.cs +++ b/cs/src/core/io/safe/InputBuffer.cs @@ -70,7 +70,7 @@ public InputBuffer Clone() /// public void SkipBytes(int count) { - position += count; + position = checked(position + count); } /// diff --git a/cs/src/core/io/safe/OutputBuffer.cs b/cs/src/core/io/safe/OutputBuffer.cs index e2a7f5dfca..7ad470042f 100644 --- a/cs/src/core/io/safe/OutputBuffer.cs +++ b/cs/src/core/io/safe/OutputBuffer.cs @@ -220,8 +220,8 @@ public virtual void WriteString(Encoding encoding, string value, int size) // Grow the buffer so that there is enough space to write 'count' bytes internal virtual void Grow(int count) { - int minLength = position + count; - length += length >> 1; + int minLength = checked(position + count); + length = checked(length + length >> 1); const int ArrayIndexMaxValue = 0x7FFFFFC7; if ((uint)length > ArrayIndexMaxValue) diff --git a/cs/src/core/protocols/CompactBinary.cs b/cs/src/core/protocols/CompactBinary.cs index 0d1c55094d..6d954cc49e 100644 --- a/cs/src/core/protocols/CompactBinary.cs +++ b/cs/src/core/protocols/CompactBinary.cs @@ -210,7 +210,8 @@ public void WriteStructBegin(Metadata metadata) lengths.RemoveFirst(); output.WriteVarUInt32(length); - PushLengthCheck(output.Position + length); + long position = checked(output.Position + length); + PushLengthCheck(position); } } @@ -466,8 +467,9 @@ public void WriteWString(string value) } else { + int byteSize = checked(value.Length * 2); WriteUInt32((UInt32)value.Length); - output.WriteString(Encoding.Unicode, value, value.Length << 1); + output.WriteString(Encoding.Unicode, value, byteSize); } } #endregion @@ -629,7 +631,7 @@ public void ReadContainerBegin(out int count, out BondDataType elementType) if (2 == version && (raw & (0x07 << 5)) != 0) count = (raw >> 5) - 1; else - count = (int)input.ReadVarUInt32(); + count = checked((int)input.ReadVarUInt32()); } /// @@ -644,7 +646,7 @@ public void ReadContainerBegin(out int count, out BondDataType keyType, out Bond { keyType = (BondDataType)input.ReadUInt8(); valueType = (BondDataType)input.ReadUInt8(); - count = (int)input.ReadVarUInt32(); + count = checked((int)input.ReadVarUInt32()); } /// @@ -776,7 +778,7 @@ public double ReadDouble() [MethodImpl(MethodImplOptions.AggressiveInlining)] public String ReadString() { - var length = (int)input.ReadVarUInt32(); + var length = checked((int)input.ReadVarUInt32()); return length == 0 ? string.Empty : input.ReadString(Encoding.UTF8, length); } @@ -787,8 +789,8 @@ public String ReadString() [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadWString() { - var length = (int)input.ReadVarUInt32(); - return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, length << 1); + var length = checked((int)(input.ReadVarUInt32() * 2)); + return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, length); } /// @@ -837,10 +839,10 @@ public void Skip(BondDataType type) input.ReadVarUInt64(); break; case (BondDataType.BT_STRING): - input.SkipBytes((int)input.ReadVarUInt32()); + input.SkipBytes(checked((int)input.ReadVarUInt32())); break; case (BondDataType.BT_WSTRING): - input.SkipBytes((int)(input.ReadVarUInt32() << 1)); + input.SkipBytes(checked((int)(input.ReadVarUInt32() *2))); break; case BondDataType.BT_LIST: case BondDataType.BT_SET: @@ -872,11 +874,11 @@ void SkipContainer() } else if (elementType == BondDataType.BT_FLOAT) { - input.SkipBytes(count * sizeof(float)); + input.SkipBytes(checked(count * sizeof(float))); } else if (elementType == BondDataType.BT_DOUBLE) { - input.SkipBytes(count * sizeof(double)); + input.SkipBytes(checked(count * sizeof(double))); } else { @@ -905,7 +907,7 @@ void SkipStruct() { if (2 == version) { - input.SkipBytes((int)input.ReadVarUInt32()); + input.SkipBytes(checked((int)input.ReadVarUInt32())); } else { diff --git a/cs/src/core/protocols/CompactBinaryCounter.cs b/cs/src/core/protocols/CompactBinaryCounter.cs index 94288a8645..09a9b5020f 100644 --- a/cs/src/core/protocols/CompactBinaryCounter.cs +++ b/cs/src/core/protocols/CompactBinaryCounter.cs @@ -47,7 +47,9 @@ private CounterStackFrame GetCurrentStackFrame() private void AddBytes(int count) { - GetCurrentStackFrame().currentLength += count; + var stackFrame = GetCurrentStackFrame(); + var length = checked(stackFrame.currentLength + count); + stackFrame.currentLength = length; } private void AddVarUInt16(ushort value) @@ -99,7 +101,7 @@ public void WriteStructEnd() if (counterStack.Count > 0) { AddVarUInt32(structLength); - AddBytes((int)structLength); + AddBytes(checked((int)structLength)); } } diff --git a/cs/src/core/protocols/FastBinary.cs b/cs/src/core/protocols/FastBinary.cs index 28548352fa..3904a4262c 100644 --- a/cs/src/core/protocols/FastBinary.cs +++ b/cs/src/core/protocols/FastBinary.cs @@ -367,8 +367,9 @@ public void WriteWString(string value) } else { + int byteSize = checked(value.Length * 2); output.WriteVarUInt32((UInt32)value.Length); - output.WriteString(Encoding.Unicode, value, value.Length << 1); + output.WriteString(Encoding.Unicode, value, byteSize); } } #endregion @@ -478,7 +479,7 @@ public void ReadFieldEnd() public void ReadContainerBegin(out int count, out BondDataType elementType) { elementType = (BondDataType)input.ReadUInt8(); - count = (int)input.ReadVarUInt32(); + count = checked((int)input.ReadVarUInt32()); } /// @@ -493,7 +494,7 @@ public void ReadContainerBegin(out int count, out BondDataType keyType, out Bond { keyType = (BondDataType)input.ReadUInt8(); valueType = (BondDataType)input.ReadUInt8(); - count = (int)input.ReadVarUInt32(); + count = checked((int)input.ReadVarUInt32()); } /// @@ -625,7 +626,7 @@ public double ReadDouble() [MethodImpl(MethodImplOptions.AggressiveInlining)] public String ReadString() { - var length = (int)input.ReadVarUInt32(); + var length = checked((int)input.ReadVarUInt32()); return length == 0 ? string.Empty : input.ReadString(Encoding.UTF8, length); } @@ -636,8 +637,8 @@ public String ReadString() [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadWString() { - var length = (int)input.ReadVarUInt32(); - return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, length << 1); + var length = checked((int)(input.ReadVarUInt32() * 2)); + return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, length); } /// @@ -686,10 +687,10 @@ public void Skip(BondDataType type) input.SkipBytes(sizeof(ulong)); break; case (BondDataType.BT_STRING): - input.SkipBytes((int)input.ReadVarUInt32()); + input.SkipBytes(checked(((int)input.ReadVarUInt32()))); break; case (BondDataType.BT_WSTRING): - input.SkipBytes((int)(input.ReadVarUInt32() << 1)); + input.SkipBytes(checked((int)(input.ReadVarUInt32()) * 2)); break; case BondDataType.BT_LIST: case BondDataType.BT_SET: @@ -724,21 +725,21 @@ void SkipContainer() break; case (BondDataType.BT_UINT16): case (BondDataType.BT_INT16): - input.SkipBytes(count * sizeof(ushort)); + input.SkipBytes(checked(count * sizeof(ushort))); break; case (BondDataType.BT_UINT32): case (BondDataType.BT_INT32): - input.SkipBytes(count * sizeof(uint)); + input.SkipBytes(checked(count * sizeof(uint))); break; case (BondDataType.BT_UINT64): case (BondDataType.BT_INT64): - input.SkipBytes(count * sizeof(ulong)); + input.SkipBytes(checked(count * sizeof(ulong))); break; case BondDataType.BT_FLOAT: - input.SkipBytes(count * sizeof(float)); + input.SkipBytes(checked(count * sizeof(float))); break; case BondDataType.BT_DOUBLE: - input.SkipBytes(count * sizeof(double)); + input.SkipBytes(checked(count * sizeof(double))); break; default: while (0 <= --count) diff --git a/cs/src/core/protocols/SimpleBinary.cs b/cs/src/core/protocols/SimpleBinary.cs index bc488fb748..03d897f96b 100644 --- a/cs/src/core/protocols/SimpleBinary.cs +++ b/cs/src/core/protocols/SimpleBinary.cs @@ -356,7 +356,7 @@ public void WriteWString(string value) else { WriteLength(value.Length); - output.WriteString(Encoding.Unicode, value, value.Length << 1); + output.WriteString(Encoding.Unicode, value, checked(value.Length * 2)); } } #endregion @@ -628,13 +628,13 @@ public void SkipString() public string ReadWString() { var length = ReadLength(); - return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, length << 1); + return length == 0 ? string.Empty : input.ReadString(Encoding.Unicode, checked(length * 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SkipWString() { - input.SkipBytes(ReadLength() << 1); + input.SkipBytes(checked(ReadLength() * 2)); } /// @@ -659,7 +659,7 @@ public void SkipBytes(int count) [MethodImpl(MethodImplOptions.AggressiveInlining)] int ReadLength() { - return (int)((version == 2) ? input.ReadVarUInt32() : input.ReadUInt32()); + return (version == 2) ? checked((int)input.ReadVarUInt32()) : checked((int)input.ReadUInt32()); } } } diff --git a/cs/src/io/unsafe/InputPointer.cs b/cs/src/io/unsafe/InputPointer.cs index a2e50827fe..e568a836e6 100644 --- a/cs/src/io/unsafe/InputPointer.cs +++ b/cs/src/io/unsafe/InputPointer.cs @@ -65,7 +65,7 @@ public long Position /// Number of bytes to skip public void SkipBytes(int count) { - position += count; + position = checked(position + count); } /// diff --git a/cs/test/core/IndexOverflowTests.cs b/cs/test/core/IndexOverflowTests.cs new file mode 100644 index 0000000000..b20b4f99b5 --- /dev/null +++ b/cs/test/core/IndexOverflowTests.cs @@ -0,0 +1,647 @@ +using Bond; +using Bond.IO.Safe; +using Bond.Protocols; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using UnitTest.Aliases; + +namespace UnitTest +{ + [TestFixture] + class IndexOverflowTests + { + #region CompactBinaryReader + [Test] + public void CompactBinaryReader_ReadContainer_NegativeCount_Throws() + { + var simpleTypesWithNegativeCount = new byte[] + { + 0xc9, + 0x0b, // list + 0x03, // subtype string + 0x41, + 0x42, + 0x43, + 0xca, + 0x0d, + 0xcc, + 0x8, // double + 0x8, // subtype set double + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + }; + + var input = new InputBuffer(simpleTypesWithNegativeCount); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + var mapWithNegativeCount = new byte[] + { + 0x0d, // map + 0x09, // key: string + 0x02, // value: bool + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x03,// str len + 0x61, + 0x62, + 0x01, + 0x00, + }; + + input = new InputBuffer(mapWithNegativeCount); + reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_ReadString_NegativeLength_Throws() + { + var stringWithNagativeLength = new byte[] + { + 0x49, // string + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x61, + }; + + var input = new InputBuffer(stringWithNagativeLength); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_ReadWString_OverflowLength_Throws() + { + var wstringWithOverflowLength = new byte[] + { + 0x72, // wstring + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x61, + 0x00, + }; + + var input = new InputBuffer(wstringWithOverflowLength); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + wstringWithOverflowLength = new byte[] + { + 0x72, // wstring + + 0x80, // count (5 byte) = int.MaxValue / 2 + 1 + 0x80, + 0x80, + 0x80, + 0x04, + + 0x61, + 0x00, + }; + + input = new InputBuffer(wstringWithOverflowLength); + reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_Skip_String_NegativeLength_Throws() + { + var data = new byte[] + { + 0x0a, + 0x49, + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + + 0x3c, + }; + + var input = new InputBuffer(data); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_Skip_WString_OverflowLength_Throws() + { + var data = new byte[] + { + 0x0a, + 0x49, + 0x06, + 0x3c, + 0x33, + 0x48, + 0x25, + 0x5b, + 0x67, + 0x72, + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + + 0x4d, + 0x00, + }; + + var input = new InputBuffer(data); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + data = new byte[] + { + 0x0a, + 0x49, + 0x06, + 0x3c, + 0x33, + 0x48, + 0x25, + 0x5b, + 0x67, + 0x72, + + 0x80, // count (5 byte) = int.MaxValue / 2 + 1 + 0x80, + 0x80, + 0x80, + 0x04, + + 0x4d, + 0x00, + }; + + input = new InputBuffer(data); + reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_SkipStruct_NegativeLength_Throws() + { + var data = new byte[] + { + 0x26, + 0x0a, + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + + 0x02, + }; + + var input = new InputBuffer(data); + var reader = new CompactBinaryReader(input, 2); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void CompactBinaryReader_SkipContainer_Float_OverflowLength_Throws() + { + var data = new byte[] + { + 0x2b, + 0x07, + + 0x80, // int.MaxValue / sizeof(float) + 1 + 0x80, + 0x80, + 0x80, + 0x02, + + 0x48, + 0x00, + }; + + var input = new InputBuffer(data); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize>>.From(reader)); + } + + [Test] + public void CompactBinaryReader_SkipContainer_Double_OverflowLength_Throws() + { + var data = new byte[] + { + 0x2b, + 0x08, + + 0x80, // int.MaxValue / sizeof(double) + 1 + 0x80, + 0x80, + 0x80, + 0x01, + + 0x48, + }; + + var input = new InputBuffer(data); + var reader = new CompactBinaryReader(input); + Assert.Throws(() => Deserialize>>.From(reader)); + } + #endregion + + #region FastBinaryReader + [Test] + public void FastBinaryReader_ReadContainer_NegativeCount_Throws() + { + var simpleTypesWithNegativeCount = new byte[] + { + 0x0b, // list of strings + 0x00, + 0x00, + 0x09, + + 0xff, // count (5 bytes) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x01, // sting len + 0x61, + }; + + var input = new InputBuffer(simpleTypesWithNegativeCount); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + var mapWithNegativeCount = new byte[] + { + 0x0d, // map (string --> bool) + 0x00, + 0x00, + 0x09, + 0x02, + + 0xff, // count (5 bytes) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x01, // string len 3 + 0x61, + 0x01, // bool + }; + + input = new InputBuffer(mapWithNegativeCount); + reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void FastBinaryReader_ReadString_NegativeLength_Throws() + { + var stringWithNagativeLength = new byte[] + { + 0x09, // string + 0x02, + 0x00, + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x61, + }; + + var input = new InputBuffer(stringWithNagativeLength); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void FastBinaryReader_ReadWString_OverflowLength_Throws() + { + var data = new byte[] + { + 0x12, // wstring + 0x03, + 0x00, + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x61, + 0x00, + }; + + var input = new InputBuffer(data); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + data = new byte[] + { + 0x12, // wstring + 0x03, + 0x00, + + 0x80, // count (5 byte) = int.MaxValue / 2 + 1 + 0x80, + 0x80, + 0x80, + 0x04, + + 0x61, + 0x00, + }; + + input = new InputBuffer(data); + reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void FastBinaryReader_Skip_String_NegativeLength_Throws() + { + var data = new byte[] + { + 0x0a, + 0x00, + 0x00, + 0x09, + 0x02, + 0x00, + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + + 0x5a, + }; + + var input = new InputBuffer(data); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + public void FastBinaryReader_Skip_WString_OverflowLength_Throws() + { + var data = new byte[] + { + 0x0a, + 0x00, + 0x00, + 0x09, + 0x02, + 0x00, + 0x27, + 0x49, + 0x39, + 0x54, + 0x36, + 0x75, + 0x29, + 0x53, + 0x43, + 0x56, + 0x3d, + 0x21, + 0x74, + 0x56, + 0x46, + 0x39, + 0x22, + 0x7a, + 0x58, + 0x34, + 0x70, + 0x2d, + 0x70, + 0x69, + 0x5b, + 0x27, + 0x23, + 0x40, + 0x32, + 0x60, + 0x2c, + 0x43, + 0x3a, + 0x3f, + 0x6c, + 0x36, + 0x55, + 0x38, + 0x7a, + 0x54, + 0x12, + 0x03, + 0x00, + + 0xff, // count -1 + 0xff, + 0xff, + 0xff, + 0xff, + + 0x3c, + 0x00, + + }; + + var input = new InputBuffer(data); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + data = new byte[] + { + 0x0a, + 0x00, + 0x00, + 0x09, + 0x02, + 0x00, + 0x27, + 0x49, + 0x39, + 0x54, + 0x36, + 0x75, + 0x29, + 0x53, + 0x43, + 0x56, + 0x3d, + 0x21, + 0x74, + 0x56, + 0x46, + 0x39, + 0x22, + 0x7a, + 0x58, + 0x34, + 0x70, + 0x2d, + 0x70, + 0x69, + 0x5b, + 0x27, + 0x23, + 0x40, + 0x32, + 0x60, + 0x2c, + 0x43, + 0x3a, + 0x3f, + 0x6c, + 0x36, + 0x55, + 0x38, + 0x7a, + 0x54, + 0x12, + 0x03, + 0x00, + + 0x80, // count (5 byte) = int.MaxValue / 2 + 1 + 0x80, + 0x80, + 0x80, + 0x04, + + 0x3c, + 0x00, + }; + + input = new InputBuffer(data); + reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + } + + [Test] + [TestCaseSource(nameof(FastBinaryOverflowingLengthPayloads))] + public void FasttBinaryReader_SkipContainer_OverflowLength_Throws(byte[] data) + { + var input = new InputBuffer(data); + var reader = new FastBinaryReader(input); + Assert.Throws(() => Deserialize>>.From(reader)); + } + + public static IEnumerable FastBinaryOverflowingLengthPayloads() + { + const int TypeIndex = 3; + byte[][] testCases = + { + new byte[]{(int)BondDataType.BT_UINT16, 0x80, 0x80, 0x80, 0x80, 0x04}, // Bytes: int.MaxValue / sizeof(Uint16) + 1 + new byte[]{(int)BondDataType.BT_UINT32, 0x80, 0x80, 0x80, 0x80, 0x02}, // Bytes: int.MaxValue / sizeof(Uint32) + 1 + new byte[]{(int)BondDataType.BT_UINT64, 0x80, 0x80, 0x80, 0x80, 0x01}, // Bytes: int.MaxValue / sizeof(Uint64) + 1 + new byte[]{(int)BondDataType.BT_FLOAT, 0x80, 0x80, 0x80, 0x80, 0x02}, // Bytes: int.MaxValue / sizeof(float) + 1 + new byte[]{(int)BondDataType.BT_DOUBLE, 0x80, 0x80, 0x80, 0x80, 0x01}, // Bytes: int.MaxValue / sizeof(double) + 1 + new byte[]{(int)BondDataType.BT_INT16, 0x80, 0x80, 0x80, 0x80, 0x04}, // Bytes: int.MaxValue / sizeof(Int16) + 1 + new byte[]{(int)BondDataType.BT_INT32, 0x80, 0x80, 0x80, 0x80, 0x02}, // Bytes: int.MaxValue / sizeof(Int32) + 1 + new byte[]{(int)BondDataType.BT_INT64, 0x80, 0x80, 0x80, 0x80, 0x01}, // Bytes: int.MaxValue / sizeof(Int64) + 1 + }; + + + foreach (var tc in testCases) + { + int idx = TypeIndex; + byte[] data = + { + 0x0b, + 0x01, + 0x00, + 0xff, // type + + 0x00, // count (5 byte) + 0x00, + 0x00, + 0x00, + 0x00, + + 0x08, + }; + + Buffer.BlockCopy( + src: tc, + srcOffset: 0, + dst: data, + dstOffset: idx, + count: tc.Length); + + yield return data; + } + } + #endregion + + #region SimpleBinaryReader + [Test] + public void SimpleBinaryReader_ReadLength_NegativeLength_Throws() + { + var f = new Foo { _str = "abc" }; + var output = new Bond.IO.Safe.OutputBuffer(16); + var writer = new SimpleBinaryWriter(output, 2); + Serialize.To(writer, f); + + + var data = new byte[] + { + 0x00, + + 0xff, // count (5 byte) + 0xff, + 0xff, + 0xff, + 0xff, + + 0x00, + 0x00, + 0x00, + 0x61, + }; + + var input = new InputBuffer(data); + var reader = new SimpleBinaryReader(input); + Assert.Throws(() => Deserialize.From(reader)); + + input = new InputBuffer(data); + reader = new SimpleBinaryReader(input, 2); + Assert.Throws(() => Deserialize.From(reader)); + } + #endregion + } +}