From d8800ae2f3699964363cc23f6f31f77a8e3a83b5 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 13 Nov 2020 12:11:31 +1300 Subject: [PATCH 1/2] Fix parsing negative Int32Value that crosses segment boundary --- .../CodedInputStreamTest.cs | 19 +++++++++++++++++++ .../ParsingPrimitivesWrappers.cs | 10 ++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs index 1fb3bb503908..0ad286f3787f 100644 --- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs +++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs @@ -343,6 +343,25 @@ public void ReadWholeMessage_VaryingBlockSizes_FromSequence() } } + [Test] + public void ReadInt32Wrapper_VariableBlockSizes() + { + byte[] rawBytes = new byte[] { 202, 1, 11, 8, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1 }; + + for (int blockSize = 1; blockSize <= rawBytes.Length; blockSize++) + { + ReadOnlySequence data = ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize); + AssertReadFromParseContext(data, (ref ParseContext ctx) => + { + ctx.ReadTag(); + + var value = ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx); + + Assert.AreEqual(-2, value); + }, true); + } + } + [Test] public void ReadHugeBlob() { diff --git a/csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs b/csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs index 126306490a09..629ec32bd81e 100644 --- a/csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs +++ b/csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs @@ -160,8 +160,11 @@ internal static class ParsingPrimitivesWrappers internal static uint? ReadUInt32Wrapper(ref ReadOnlySpan buffer, ref ParserInternalState state) { - // length:1 + tag:1 + value:5(varint32-max) = 7 bytes - if (state.bufferPos + 7 <= state.bufferSize) + // field=1, type=varint = tag of 8 + const int expectedTag = 8; + // length:1 + tag:1 + value:10(varint64-max) = 12 bytes + // Value can be 64 bits for negative integers + if (state.bufferPos + 12 <= state.bufferSize) { // The entire wrapper message is already contained in `buffer`. int pos0 = state.bufferPos; @@ -177,8 +180,7 @@ internal static class ParsingPrimitivesWrappers return ReadUInt32WrapperSlow(ref buffer, ref state); } int finalBufferPos = state.bufferPos + length; - // field=1, type=varint = tag of 8 - if (buffer[state.bufferPos++] != 8) + if (buffer[state.bufferPos++] != expectedTag) { state.bufferPos = pos0; return ReadUInt32WrapperSlow(ref buffer, ref state); From fdf4ef62a56e4c628aeda3921da1f11b68fd2d71 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 13 Nov 2020 12:17:24 +1300 Subject: [PATCH 2/2] Additional test --- .../WellKnownTypes/WrappersTest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs index 94c4746a80dc..5b0e5e8fc1cb 100644 --- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs +++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs @@ -87,6 +87,28 @@ public void NonDefaultSingleValues() }); } + [Test] + public void NegativeSingleValues() + { + var message = new TestWellKnownTypes + { + FloatField = -12.5f, + DoubleField = -12.25d, + Int32Field = -1, + Int64Field = -2 + }; + + MessageParsingHelpers.AssertWritingMessage(message); + + MessageParsingHelpers.AssertRoundtrip(TestWellKnownTypes.Parser, message, parsed => + { + Assert.AreEqual(-12.5f, parsed.FloatField); + Assert.AreEqual(-12.25d, parsed.DoubleField); + Assert.AreEqual(-1, parsed.Int32Field); + Assert.AreEqual(-2L, parsed.Int64Field); + }); + } + [Test] public void NonNullDefaultIsPreservedThroughSerialization() {