From 9bdc848832b6f6e27ea4389a72c566ec43329114 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Mon, 15 Feb 2016 11:58:01 +0000 Subject: Validate that end-group tags match their corresponding start-group tags This detects: - An end-group tag with the wrong field number (doesn't match the start-group field) - An end-group tag with no preceding start-group tag Fixes issue #688. --- csharp/src/Google.Protobuf/CodedInputStream.cs | 32 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'csharp/src/Google.Protobuf/CodedInputStream.cs') diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index 91bed8e3..1c02d951 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -349,6 +349,14 @@ namespace Google.Protobuf /// This should be called directly after , when /// the caller wishes to skip an unknown field. /// + /// + /// This method throws if the last-read tag was an end-group tag. + /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the + /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly + /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag. + /// + /// The last tag was an end-group tag + /// The last read operation read to the end of the logical stream public void SkipLastField() { if (lastTag == 0) @@ -358,11 +366,11 @@ namespace Google.Protobuf switch (WireFormat.GetTagWireType(lastTag)) { case WireFormat.WireType.StartGroup: - SkipGroup(); + SkipGroup(lastTag); break; case WireFormat.WireType.EndGroup: - // Just ignore; there's no data following the tag. - break; + throw new InvalidProtocolBufferException( + "SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing"); case WireFormat.WireType.Fixed32: ReadFixed32(); break; @@ -379,7 +387,7 @@ namespace Google.Protobuf } } - private void SkipGroup() + private void SkipGroup(uint startGroupTag) { // Note: Currently we expect this to be the way that groups are read. We could put the recursion // depth changes into the ReadTag method instead, potentially... @@ -389,16 +397,28 @@ namespace Google.Protobuf throw InvalidProtocolBufferException.RecursionLimitExceeded(); } uint tag; - do + while (true) { tag = ReadTag(); if (tag == 0) { throw InvalidProtocolBufferException.TruncatedMessage(); } + // Can't call SkipLastField for this case- that would throw. + if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup) + { + break; + } // This recursion will allow us to handle nested groups. SkipLastField(); - } while (WireFormat.GetTagWireType(tag) != WireFormat.WireType.EndGroup); + } + int startField = WireFormat.GetTagFieldNumber(startGroupTag); + int endField = WireFormat.GetTagFieldNumber(tag); + if (startField != endField) + { + throw new InvalidProtocolBufferException( + $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}"); + } recursionDepth--; } -- cgit v1.2.3