diff --git a/protos/extest/unittest_extras_xmltest.proto b/protos/extest/unittest_extras_xmltest.proto index 4a3b881047..331c6265f4 100644 --- a/protos/extest/unittest_extras_xmltest.proto +++ b/protos/extest/unittest_extras_xmltest.proto @@ -1,53 +1,57 @@ -// Additional options required for C# generation. File from copyright -// line onwards is as per original distribution. -import "google/protobuf/csharp_options.proto"; -option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestXmlSerializerTestProtoFile"; - -package protobuf_unittest_extra; - -option optimize_for = SPEED; - -enum EnumOptions { - ONE = 0; - TWO = 1; - THREE = 2; -} - -message TestXmlChild -{ - repeated EnumOptions options = 3; - optional bytes binary = 4; -} - -message TestXmlNoFields { -} - -message TestXmlMessage { - - optional int64 number = 6; - repeated int32 numbers = 2; - optional string text = 3; - repeated string textlines = 700; - optional bool valid = 5; - - optional TestXmlChild child = 1; - repeated group Children = 401 - { - repeated EnumOptions options = 3; +// Additional options required for C# generation. File from copyright +// line onwards is as per original distribution. +import "google/protobuf/csharp_options.proto"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestXmlSerializerTestProtoFile"; + +package protobuf_unittest_extra; + +option optimize_for = SPEED; + +enum EnumOptions { + ONE = 0; + TWO = 1; + THREE = 2; +} + +message TestXmlChild +{ + repeated EnumOptions options = 3; + optional bytes binary = 4; +} + +message TestXmlNoFields { +} + +message TestXmlRescursive { + optional TestXmlRescursive child = 1; +} + +message TestXmlMessage { + + optional int64 number = 6; + repeated int32 numbers = 2; + optional string text = 3; + repeated string textlines = 700; + optional bool valid = 5; + + optional TestXmlChild child = 1; + repeated group Children = 401 + { + repeated EnumOptions options = 3; optional bytes binary = 4; } - - extensions 100 to 199; -} - -message TestXmlExtension { - required int32 number = 1; -} - -extend TestXmlMessage { - optional EnumOptions extension_enum = 101; - optional string extension_text = 102; - repeated int32 extension_number = 103 [packed = true]; - optional TestXmlExtension extension_message = 199; -} + + extensions 100 to 199; +} + +message TestXmlExtension { + required int32 number = 1; +} + +extend TestXmlMessage { + optional EnumOptions extension_enum = 101; + optional string extension_text = 102; + repeated int32 extension_number = 103 [packed = true]; + optional TestXmlExtension extension_message = 199; +} diff --git a/src/ProtocolBuffers.Test/TestProtos/UnitTestXmlSerializerTestProtoFile.cs b/src/ProtocolBuffers.Test/TestProtos/UnitTestXmlSerializerTestProtoFile.cs index 441ea65a05..c050766736 100644 --- a/src/ProtocolBuffers.Test/TestProtos/UnitTestXmlSerializerTestProtoFile.cs +++ b/src/ProtocolBuffers.Test/TestProtos/UnitTestXmlSerializerTestProtoFile.cs @@ -35,6 +35,8 @@ namespace Google.ProtocolBuffers.TestProtos { internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_extra_TestXmlChild__FieldAccessorTable; internal static pbd::MessageDescriptor internal__static_protobuf_unittest_extra_TestXmlNoFields__Descriptor; internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_extra_TestXmlNoFields__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_protobuf_unittest_extra_TestXmlRescursive__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_extra_TestXmlRescursive__FieldAccessorTable; internal static pbd::MessageDescriptor internal__static_protobuf_unittest_extra_TestXmlMessage__Descriptor; internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_extra_TestXmlMessage__FieldAccessorTable; internal static pbd::MessageDescriptor internal__static_protobuf_unittest_extra_TestXmlMessage_Children__Descriptor; @@ -54,25 +56,27 @@ namespace Google.ProtocolBuffers.TestProtos { "YnVmX3VuaXR0ZXN0X2V4dHJhGiRnb29nbGUvcHJvdG9idWYvY3NoYXJwX29w" + "dGlvbnMucHJvdG8iVQoMVGVzdFhtbENoaWxkEjUKB29wdGlvbnMYAyADKA4y" + "JC5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5FbnVtT3B0aW9ucxIOCgZiaW5h" + - "cnkYBCABKAwiEQoPVGVzdFhtbE5vRmllbGRzIrcCCg5UZXN0WG1sTWVzc2Fn" + - "ZRIOCgZudW1iZXIYBiABKAMSDwoHbnVtYmVycxgCIAMoBRIMCgR0ZXh0GAMg" + - "ASgJEhIKCXRleHRsaW5lcxi8BSADKAkSDQoFdmFsaWQYBSABKAgSNAoFY2hp" + - "bGQYASABKAsyJS5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0WG1sQ2hp" + - "bGQSQwoIY2hpbGRyZW4YkQMgAygKMjAucHJvdG9idWZfdW5pdHRlc3RfZXh0" + - "cmEuVGVzdFhtbE1lc3NhZ2UuQ2hpbGRyZW4aUQoIQ2hpbGRyZW4SNQoHb3B0" + - "aW9ucxgDIAMoDjIkLnByb3RvYnVmX3VuaXR0ZXN0X2V4dHJhLkVudW1PcHRp" + - "b25zEg4KBmJpbmFyeRgEIAEoDCoFCGQQyAEiIgoQVGVzdFhtbEV4dGVuc2lv" + - "bhIOCgZudW1iZXIYASACKAUqKgoLRW51bU9wdGlvbnMSBwoDT05FEAASBwoD" + - "VFdPEAESCQoFVEhSRUUQAjplCg5leHRlbnNpb25fZW51bRInLnByb3RvYnVm" + - "X3VuaXR0ZXN0X2V4dHJhLlRlc3RYbWxNZXNzYWdlGGUgASgOMiQucHJvdG9i" + - "dWZfdW5pdHRlc3RfZXh0cmEuRW51bU9wdGlvbnM6PwoOZXh0ZW5zaW9uX3Rl" + - "eHQSJy5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0WG1sTWVzc2FnZRhm" + - "IAEoCTpFChBleHRlbnNpb25fbnVtYmVyEicucHJvdG9idWZfdW5pdHRlc3Rf" + - "ZXh0cmEuVGVzdFhtbE1lc3NhZ2UYZyADKAVCAhABOm4KEWV4dGVuc2lvbl9t" + - "ZXNzYWdlEicucHJvdG9idWZfdW5pdHRlc3RfZXh0cmEuVGVzdFhtbE1lc3Nh" + - "Z2UYxwEgASgLMikucHJvdG9idWZfdW5pdHRlc3RfZXh0cmEuVGVzdFhtbEV4" + - "dGVuc2lvbkJMSAHCPkcKIUdvb2dsZS5Qcm90b2NvbEJ1ZmZlcnMuVGVzdFBy" + - "b3RvcxIiVW5pdFRlc3RYbWxTZXJpYWxpemVyVGVzdFByb3RvRmlsZQ=="); + "cnkYBCABKAwiEQoPVGVzdFhtbE5vRmllbGRzIk4KEVRlc3RYbWxSZXNjdXJz" + + "aXZlEjkKBWNoaWxkGAEgASgLMioucHJvdG9idWZfdW5pdHRlc3RfZXh0cmEu" + + "VGVzdFhtbFJlc2N1cnNpdmUitwIKDlRlc3RYbWxNZXNzYWdlEg4KBm51bWJl" + + "chgGIAEoAxIPCgdudW1iZXJzGAIgAygFEgwKBHRleHQYAyABKAkSEgoJdGV4" + + "dGxpbmVzGLwFIAMoCRINCgV2YWxpZBgFIAEoCBI0CgVjaGlsZBgBIAEoCzIl" + + "LnByb3RvYnVmX3VuaXR0ZXN0X2V4dHJhLlRlc3RYbWxDaGlsZBJDCghjaGls" + + "ZHJlbhiRAyADKAoyMC5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0WG1s" + + "TWVzc2FnZS5DaGlsZHJlbhpRCghDaGlsZHJlbhI1CgdvcHRpb25zGAMgAygO" + + "MiQucHJvdG9idWZfdW5pdHRlc3RfZXh0cmEuRW51bU9wdGlvbnMSDgoGYmlu" + + "YXJ5GAQgASgMKgUIZBDIASIiChBUZXN0WG1sRXh0ZW5zaW9uEg4KBm51bWJl" + + "chgBIAIoBSoqCgtFbnVtT3B0aW9ucxIHCgNPTkUQABIHCgNUV08QARIJCgVU" + + "SFJFRRACOmUKDmV4dGVuc2lvbl9lbnVtEicucHJvdG9idWZfdW5pdHRlc3Rf" + + "ZXh0cmEuVGVzdFhtbE1lc3NhZ2UYZSABKA4yJC5wcm90b2J1Zl91bml0dGVz" + + "dF9leHRyYS5FbnVtT3B0aW9uczo/Cg5leHRlbnNpb25fdGV4dBInLnByb3Rv" + + "YnVmX3VuaXR0ZXN0X2V4dHJhLlRlc3RYbWxNZXNzYWdlGGYgASgJOkUKEGV4" + + "dGVuc2lvbl9udW1iZXISJy5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0" + + "WG1sTWVzc2FnZRhnIAMoBUICEAE6bgoRZXh0ZW5zaW9uX21lc3NhZ2USJy5w" + + "cm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0WG1sTWVzc2FnZRjHASABKAsy" + + "KS5wcm90b2J1Zl91bml0dGVzdF9leHRyYS5UZXN0WG1sRXh0ZW5zaW9uQkxI" + + "AcI+RwohR29vZ2xlLlByb3RvY29sQnVmZmVycy5UZXN0UHJvdG9zEiJVbml0" + + "VGVzdFhtbFNlcmlhbGl6ZXJUZXN0UHJvdG9GaWxl"); pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { descriptor = root; internal__static_protobuf_unittest_extra_TestXmlChild__Descriptor = Descriptor.MessageTypes[0]; @@ -83,7 +87,11 @@ namespace Google.ProtocolBuffers.TestProtos { internal__static_protobuf_unittest_extra_TestXmlNoFields__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable(internal__static_protobuf_unittest_extra_TestXmlNoFields__Descriptor, new string[] { }); - internal__static_protobuf_unittest_extra_TestXmlMessage__Descriptor = Descriptor.MessageTypes[2]; + internal__static_protobuf_unittest_extra_TestXmlRescursive__Descriptor = Descriptor.MessageTypes[2]; + internal__static_protobuf_unittest_extra_TestXmlRescursive__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_protobuf_unittest_extra_TestXmlRescursive__Descriptor, + new string[] { "Child", }); + internal__static_protobuf_unittest_extra_TestXmlMessage__Descriptor = Descriptor.MessageTypes[3]; internal__static_protobuf_unittest_extra_TestXmlMessage__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable(internal__static_protobuf_unittest_extra_TestXmlMessage__Descriptor, new string[] { "Number", "Numbers", "Text", "Textlines", "Valid", "Child", "Children", }); @@ -91,7 +99,7 @@ namespace Google.ProtocolBuffers.TestProtos { internal__static_protobuf_unittest_extra_TestXmlMessage_Children__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable(internal__static_protobuf_unittest_extra_TestXmlMessage_Children__Descriptor, new string[] { "Options", "Binary", }); - internal__static_protobuf_unittest_extra_TestXmlExtension__Descriptor = Descriptor.MessageTypes[3]; + internal__static_protobuf_unittest_extra_TestXmlExtension__Descriptor = Descriptor.MessageTypes[4]; internal__static_protobuf_unittest_extra_TestXmlExtension__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable(internal__static_protobuf_unittest_extra_TestXmlExtension__Descriptor, new string[] { "Number", }); @@ -631,6 +639,268 @@ namespace Google.ProtocolBuffers.TestProtos { } } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProtoGen", "2.3.0.277")] + public sealed partial class TestXmlRescursive : pb::GeneratedMessage { + private static readonly TestXmlRescursive defaultInstance = new Builder().BuildPartial(); + private static readonly string[] _testXmlRescursiveFieldNames = new string[] { "child" }; + private static readonly uint[] _testXmlRescursiveFieldTags = new uint[] { 10 }; + public static TestXmlRescursive DefaultInstance { + get { return defaultInstance; } + } + + public override TestXmlRescursive DefaultInstanceForType { + get { return defaultInstance; } + } + + protected override TestXmlRescursive ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::Google.ProtocolBuffers.TestProtos.UnitTestXmlSerializerTestProtoFile.internal__static_protobuf_unittest_extra_TestXmlRescursive__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::Google.ProtocolBuffers.TestProtos.UnitTestXmlSerializerTestProtoFile.internal__static_protobuf_unittest_extra_TestXmlRescursive__FieldAccessorTable; } + } + + public const int ChildFieldNumber = 1; + private bool hasChild; + private global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive child_ = global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.DefaultInstance; + public bool HasChild { + get { return hasChild; } + } + public global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive Child { + get { return child_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _testXmlRescursiveFieldNames; + if (hasChild) { + output.WriteMessage(1, field_names[0], Child); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasChild) { + size += pb::CodedOutputStream.ComputeMessageSize(1, Child); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static TestXmlRescursive ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static TestXmlRescursive ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static TestXmlRescursive ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static TestXmlRescursive ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(TestXmlRescursive prototype) { + return (Builder) new Builder().MergeFrom(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProtoGen", "2.3.0.277")] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() {} + + TestXmlRescursive result = new TestXmlRescursive(); + + protected override TestXmlRescursive MessageBeingBuilt { + get { return result; } + } + + public override Builder Clear() { + result = new TestXmlRescursive(); + return this; + } + + public override Builder Clone() { + return new Builder().MergeFrom(result); + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.Descriptor; } + } + + public override TestXmlRescursive DefaultInstanceForType { + get { return global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.DefaultInstance; } + } + + public override TestXmlRescursive BuildPartial() { + if (result == null) { + throw new global::System.InvalidOperationException("build() has already been called on this Builder"); + } + TestXmlRescursive returnMe = result; + result = null; + return returnMe; + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is TestXmlRescursive) { + return MergeFrom((TestXmlRescursive) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(TestXmlRescursive other) { + if (other == global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.DefaultInstance) return this; + if (other.HasChild) { + MergeChild(other.Child); + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_testXmlRescursiveFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _testXmlRescursiveFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 10: { + global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.Builder subBuilder = global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.CreateBuilder(); + if (result.hasChild) { + subBuilder.MergeFrom(Child); + } + input.ReadMessage(subBuilder, extensionRegistry); + Child = subBuilder.BuildPartial(); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasChild { + get { return result.hasChild; } + } + public global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive Child { + get { return result.Child; } + set { SetChild(value); } + } + public Builder SetChild(global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive value) { + pb::ThrowHelper.ThrowIfNull(value, "value"); + result.hasChild = true; + result.child_ = value; + return this; + } + public Builder SetChild(global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.Builder builderForValue) { + pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue"); + result.hasChild = true; + result.child_ = builderForValue.Build(); + return this; + } + public Builder MergeChild(global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive value) { + pb::ThrowHelper.ThrowIfNull(value, "value"); + if (result.hasChild && + result.child_ != global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.DefaultInstance) { + result.child_ = global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.CreateBuilder(result.child_).MergeFrom(value).BuildPartial(); + } else { + result.child_ = value; + } + result.hasChild = true; + return this; + } + public Builder ClearChild() { + result.hasChild = false; + result.child_ = global::Google.ProtocolBuffers.TestProtos.TestXmlRescursive.DefaultInstance; + return this; + } + } + static TestXmlRescursive() { + object.ReferenceEquals(global::Google.ProtocolBuffers.TestProtos.UnitTestXmlSerializerTestProtoFile.Descriptor, null); + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProtoGen", "2.3.0.277")] diff --git a/src/ProtocolBuffers.Test/TestWriterFormatJson.cs b/src/ProtocolBuffers.Test/TestWriterFormatJson.cs index 052d8f2b4b..fe6c22b478 100644 --- a/src/ProtocolBuffers.Test/TestWriterFormatJson.cs +++ b/src/ProtocolBuffers.Test/TestWriterFormatJson.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; using Google.ProtocolBuffers.Serialization; using NUnit.Framework; using Google.ProtocolBuffers.TestProtos; @@ -336,6 +337,14 @@ namespace Google.ProtocolBuffers Assert.AreEqual(3, ordinal); Assert.AreEqual(3, builder.TextlinesCount); } + [Test,ExpectedException(typeof(InvalidProtocolBufferException))] + public void TestRecursiveLimit() + { + StringBuilder sb = new StringBuilder(8192); + for (int i = 0; i < 80; i++) + sb.Append("{\"child\":"); + TestXmlRescursive msg = TestXmlRescursive.ParseFromJson(sb.ToString()); + } [Test, ExpectedException(typeof(FormatException))] public void FailWithEmptyText() { diff --git a/src/ProtocolBuffers.Test/TestWriterFormatXml.cs b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs index b5eb60bfee..acad6f1371 100644 --- a/src/ProtocolBuffers.Test/TestWriterFormatXml.cs +++ b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs @@ -324,5 +324,13 @@ namespace Google.ProtocolBuffers TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build(); Assert.AreEqual(message, copy); } + [Test, ExpectedException(typeof(InvalidProtocolBufferException))] + public void TestRecursiveLimit() + { + StringBuilder sb = new StringBuilder(8192); + for (int i = 0; i < 80; i++) + sb.Append(""); + TestXmlRescursive msg = TestXmlRescursive.ParseFromXml("child", XmlReader.Create(new StringReader(sb.ToString()))); + } } } diff --git a/src/ProtocolBuffers/Serialization/AbstractReader.cs b/src/ProtocolBuffers/Serialization/AbstractReader.cs index 8e4030e600..eb9baae15b 100644 --- a/src/ProtocolBuffers/Serialization/AbstractReader.cs +++ b/src/ProtocolBuffers/Serialization/AbstractReader.cs @@ -12,6 +12,9 @@ namespace Google.ProtocolBuffers.Serialization /// public abstract class AbstractReader : ICodedInputStream { + const int MaxDepth = CodedInputStream.DefaultRecursionLimit; + protected int _depth; + /// /// Merges the contents of stream into the provided message builder /// @@ -330,13 +333,23 @@ namespace Google.ProtocolBuffers.Serialization { return Read(ref value); } void ICodedInputStream.ReadGroup(int fieldNumber, IBuilderLite builder, ExtensionRegistry extensionRegistry) - { ReadGroup(builder, extensionRegistry); } + { + if (_depth++ > MaxDepth) + throw InvalidProtocolBufferException.RecursionLimitExceeded(); + ReadGroup(builder, extensionRegistry); + _depth--; + } void ICodedInputStream.ReadUnknownGroup(int fieldNumber, IBuilderLite builder) { throw new NotSupportedException(); } void ICodedInputStream.ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry) - { ReadMessage(builder, extensionRegistry); } + { + if (_depth++ > MaxDepth) + throw InvalidProtocolBufferException.RecursionLimitExceeded(); + ReadMessage(builder, extensionRegistry); + _depth--; + } bool ICodedInputStream.ReadBytes(ref ByteString value) { return Read(ref value); } @@ -439,10 +452,20 @@ namespace Google.ProtocolBuffers.Serialization } void ICodedInputStream.ReadMessageArray(uint fieldTag, string fieldName, ICollection list, T messageType, ExtensionRegistry registry) - { ReadMessageArray(fieldName, list, messageType, registry); } + { + if (_depth++ > MaxDepth) + throw InvalidProtocolBufferException.RecursionLimitExceeded(); + ReadMessageArray(fieldName, list, messageType, registry); + _depth--; + } void ICodedInputStream.ReadGroupArray(uint fieldTag, string fieldName, ICollection list, T messageType, ExtensionRegistry registry) - { ReadGroupArray(fieldName, list, messageType, registry); } + { + if (_depth++ > MaxDepth) + throw InvalidProtocolBufferException.RecursionLimitExceeded(); + ReadGroupArray(fieldName, list, messageType, registry); + _depth--; + } bool ICodedInputStream.ReadPrimitiveField(FieldType fieldType, ref object value) { return ReadField(fieldType, ref value); } diff --git a/src/ProtocolBuffers/Serialization/DictionaryWriter.cs b/src/ProtocolBuffers/Serialization/DictionaryWriter.cs index 596a926291..ccfa31f0cc 100644 --- a/src/ProtocolBuffers/Serialization/DictionaryWriter.cs +++ b/src/ProtocolBuffers/Serialization/DictionaryWriter.cs @@ -15,7 +15,7 @@ namespace Google.ProtocolBuffers.Serialization /// Constructs a writer using a new dictionary /// public DictionaryWriter() - : this(new Dictionary()) + : this(new Dictionary(StringComparer.Ordinal)) { } /// @@ -27,6 +27,14 @@ namespace Google.ProtocolBuffers.Serialization _output = output; } + /// + /// Creates the dictionary instance for a child message. + /// + protected virtual DictionaryWriter Create() + { + return new DictionaryWriter(); + } + /// /// Accesses the dictionary that is backing this writer /// @@ -119,7 +127,7 @@ namespace Google.ProtocolBuffers.Serialization /// protected override void WriteMessageOrGroup(string field, IMessageLite message) { - DictionaryWriter writer = new DictionaryWriter(); + DictionaryWriter writer = Create(); writer.WriteMessage(message); _output[field] = writer.ToDictionary(); @@ -146,7 +154,7 @@ namespace Google.ProtocolBuffers.Serialization case FieldType.Group: case FieldType.Message: { - DictionaryWriter writer = new DictionaryWriter(); + DictionaryWriter writer = Create(); writer.WriteMessage((IMessageLite)o); objects.Add(writer.ToDictionary()); } diff --git a/src/ProtocolBuffers/Serialization/XmlFormatReader.cs b/src/ProtocolBuffers/Serialization/XmlFormatReader.cs index 241c554a4c..fcd83fb39e 100644 --- a/src/ProtocolBuffers/Serialization/XmlFormatReader.cs +++ b/src/ProtocolBuffers/Serialization/XmlFormatReader.cs @@ -72,7 +72,11 @@ namespace Google.ProtocolBuffers.Serialization private XmlFormatReader CloneWith(XmlReader rdr) { - return new XmlFormatReader(rdr).SetOptions(Options); + XmlFormatReader copy = new XmlFormatReader(rdr).SetOptions(Options); + copy._rootElementName = _rootElementName; + copy._depth = _depth; + return copy; + } private void NextElement() {