1ffe3c632Sopenharmony_ci#region Copyright notice and license 2ffe3c632Sopenharmony_ci// Protocol Buffers - Google's data interchange format 3ffe3c632Sopenharmony_ci// Copyright 2008 Google Inc. All rights reserved. 4ffe3c632Sopenharmony_ci// https://developers.google.com/protocol-buffers/ 5ffe3c632Sopenharmony_ci// 6ffe3c632Sopenharmony_ci// Redistribution and use in source and binary forms, with or without 7ffe3c632Sopenharmony_ci// modification, are permitted provided that the following conditions are 8ffe3c632Sopenharmony_ci// met: 9ffe3c632Sopenharmony_ci// 10ffe3c632Sopenharmony_ci// * Redistributions of source code must retain the above copyright 11ffe3c632Sopenharmony_ci// notice, this list of conditions and the following disclaimer. 12ffe3c632Sopenharmony_ci// * Redistributions in binary form must reproduce the above 13ffe3c632Sopenharmony_ci// copyright notice, this list of conditions and the following disclaimer 14ffe3c632Sopenharmony_ci// in the documentation and/or other materials provided with the 15ffe3c632Sopenharmony_ci// distribution. 16ffe3c632Sopenharmony_ci// * Neither the name of Google Inc. nor the names of its 17ffe3c632Sopenharmony_ci// contributors may be used to endorse or promote products derived from 18ffe3c632Sopenharmony_ci// this software without specific prior written permission. 19ffe3c632Sopenharmony_ci// 20ffe3c632Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21ffe3c632Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22ffe3c632Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23ffe3c632Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24ffe3c632Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25ffe3c632Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26ffe3c632Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27ffe3c632Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28ffe3c632Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29ffe3c632Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30ffe3c632Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31ffe3c632Sopenharmony_ci#endregion 32ffe3c632Sopenharmony_ci 33ffe3c632Sopenharmony_ciusing System; 34ffe3c632Sopenharmony_ciusing System.Buffers; 35ffe3c632Sopenharmony_ciusing System.IO; 36ffe3c632Sopenharmony_ciusing System.Runtime.CompilerServices; 37ffe3c632Sopenharmony_ciusing System.Security; 38ffe3c632Sopenharmony_ci 39ffe3c632Sopenharmony_cinamespace Google.Protobuf 40ffe3c632Sopenharmony_ci{ 41ffe3c632Sopenharmony_ci /// <summary> 42ffe3c632Sopenharmony_ci /// Reading and skipping messages / groups 43ffe3c632Sopenharmony_ci /// </summary> 44ffe3c632Sopenharmony_ci [SecuritySafeCritical] 45ffe3c632Sopenharmony_ci internal static class ParsingPrimitivesMessages 46ffe3c632Sopenharmony_ci { 47ffe3c632Sopenharmony_ci public static void SkipLastField(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state) 48ffe3c632Sopenharmony_ci { 49ffe3c632Sopenharmony_ci if (state.lastTag == 0) 50ffe3c632Sopenharmony_ci { 51ffe3c632Sopenharmony_ci throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream"); 52ffe3c632Sopenharmony_ci } 53ffe3c632Sopenharmony_ci switch (WireFormat.GetTagWireType(state.lastTag)) 54ffe3c632Sopenharmony_ci { 55ffe3c632Sopenharmony_ci case WireFormat.WireType.StartGroup: 56ffe3c632Sopenharmony_ci SkipGroup(ref buffer, ref state, state.lastTag); 57ffe3c632Sopenharmony_ci break; 58ffe3c632Sopenharmony_ci case WireFormat.WireType.EndGroup: 59ffe3c632Sopenharmony_ci throw new InvalidProtocolBufferException( 60ffe3c632Sopenharmony_ci "SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing"); 61ffe3c632Sopenharmony_ci case WireFormat.WireType.Fixed32: 62ffe3c632Sopenharmony_ci ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 63ffe3c632Sopenharmony_ci break; 64ffe3c632Sopenharmony_ci case WireFormat.WireType.Fixed64: 65ffe3c632Sopenharmony_ci ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 66ffe3c632Sopenharmony_ci break; 67ffe3c632Sopenharmony_ci case WireFormat.WireType.LengthDelimited: 68ffe3c632Sopenharmony_ci var length = ParsingPrimitives.ParseLength(ref buffer, ref state); 69ffe3c632Sopenharmony_ci ParsingPrimitives.SkipRawBytes(ref buffer, ref state, length); 70ffe3c632Sopenharmony_ci break; 71ffe3c632Sopenharmony_ci case WireFormat.WireType.Varint: 72ffe3c632Sopenharmony_ci ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 73ffe3c632Sopenharmony_ci break; 74ffe3c632Sopenharmony_ci } 75ffe3c632Sopenharmony_ci } 76ffe3c632Sopenharmony_ci 77ffe3c632Sopenharmony_ci /// <summary> 78ffe3c632Sopenharmony_ci /// Skip a group. 79ffe3c632Sopenharmony_ci /// </summary> 80ffe3c632Sopenharmony_ci public static void SkipGroup(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint startGroupTag) 81ffe3c632Sopenharmony_ci { 82ffe3c632Sopenharmony_ci // Note: Currently we expect this to be the way that groups are read. We could put the recursion 83ffe3c632Sopenharmony_ci // depth changes into the ReadTag method instead, potentially... 84ffe3c632Sopenharmony_ci state.recursionDepth++; 85ffe3c632Sopenharmony_ci if (state.recursionDepth >= state.recursionLimit) 86ffe3c632Sopenharmony_ci { 87ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.RecursionLimitExceeded(); 88ffe3c632Sopenharmony_ci } 89ffe3c632Sopenharmony_ci uint tag; 90ffe3c632Sopenharmony_ci while (true) 91ffe3c632Sopenharmony_ci { 92ffe3c632Sopenharmony_ci tag = ParsingPrimitives.ParseTag(ref buffer, ref state); 93ffe3c632Sopenharmony_ci if (tag == 0) 94ffe3c632Sopenharmony_ci { 95ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.TruncatedMessage(); 96ffe3c632Sopenharmony_ci } 97ffe3c632Sopenharmony_ci // Can't call SkipLastField for this case- that would throw. 98ffe3c632Sopenharmony_ci if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup) 99ffe3c632Sopenharmony_ci { 100ffe3c632Sopenharmony_ci break; 101ffe3c632Sopenharmony_ci } 102ffe3c632Sopenharmony_ci // This recursion will allow us to handle nested groups. 103ffe3c632Sopenharmony_ci SkipLastField(ref buffer, ref state); 104ffe3c632Sopenharmony_ci } 105ffe3c632Sopenharmony_ci int startField = WireFormat.GetTagFieldNumber(startGroupTag); 106ffe3c632Sopenharmony_ci int endField = WireFormat.GetTagFieldNumber(tag); 107ffe3c632Sopenharmony_ci if (startField != endField) 108ffe3c632Sopenharmony_ci { 109ffe3c632Sopenharmony_ci throw new InvalidProtocolBufferException( 110ffe3c632Sopenharmony_ci $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}"); 111ffe3c632Sopenharmony_ci } 112ffe3c632Sopenharmony_ci state.recursionDepth--; 113ffe3c632Sopenharmony_ci } 114ffe3c632Sopenharmony_ci 115ffe3c632Sopenharmony_ci public static void ReadMessage(ref ParseContext ctx, IMessage message) 116ffe3c632Sopenharmony_ci { 117ffe3c632Sopenharmony_ci int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state); 118ffe3c632Sopenharmony_ci if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 119ffe3c632Sopenharmony_ci { 120ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.RecursionLimitExceeded(); 121ffe3c632Sopenharmony_ci } 122ffe3c632Sopenharmony_ci int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length); 123ffe3c632Sopenharmony_ci ++ctx.state.recursionDepth; 124ffe3c632Sopenharmony_ci 125ffe3c632Sopenharmony_ci ReadRawMessage(ref ctx, message); 126ffe3c632Sopenharmony_ci 127ffe3c632Sopenharmony_ci CheckReadEndOfStreamTag(ref ctx.state); 128ffe3c632Sopenharmony_ci // Check that we've read exactly as much data as expected. 129ffe3c632Sopenharmony_ci if (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state)) 130ffe3c632Sopenharmony_ci { 131ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.TruncatedMessage(); 132ffe3c632Sopenharmony_ci } 133ffe3c632Sopenharmony_ci --ctx.state.recursionDepth; 134ffe3c632Sopenharmony_ci SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit); 135ffe3c632Sopenharmony_ci } 136ffe3c632Sopenharmony_ci 137ffe3c632Sopenharmony_ci public static void ReadGroup(ref ParseContext ctx, IMessage message) 138ffe3c632Sopenharmony_ci { 139ffe3c632Sopenharmony_ci if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 140ffe3c632Sopenharmony_ci { 141ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.RecursionLimitExceeded(); 142ffe3c632Sopenharmony_ci } 143ffe3c632Sopenharmony_ci ++ctx.state.recursionDepth; 144ffe3c632Sopenharmony_ci 145ffe3c632Sopenharmony_ci uint tag = ctx.state.lastTag; 146ffe3c632Sopenharmony_ci int fieldNumber = WireFormat.GetTagFieldNumber(tag); 147ffe3c632Sopenharmony_ci ReadRawMessage(ref ctx, message); 148ffe3c632Sopenharmony_ci CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); 149ffe3c632Sopenharmony_ci 150ffe3c632Sopenharmony_ci --ctx.state.recursionDepth; 151ffe3c632Sopenharmony_ci } 152ffe3c632Sopenharmony_ci 153ffe3c632Sopenharmony_ci public static void ReadGroup(ref ParseContext ctx, int fieldNumber, UnknownFieldSet set) 154ffe3c632Sopenharmony_ci { 155ffe3c632Sopenharmony_ci if (ctx.state.recursionDepth >= ctx.state.recursionLimit) 156ffe3c632Sopenharmony_ci { 157ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.RecursionLimitExceeded(); 158ffe3c632Sopenharmony_ci } 159ffe3c632Sopenharmony_ci ++ctx.state.recursionDepth; 160ffe3c632Sopenharmony_ci 161ffe3c632Sopenharmony_ci set.MergeGroupFrom(ref ctx); 162ffe3c632Sopenharmony_ci CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); 163ffe3c632Sopenharmony_ci 164ffe3c632Sopenharmony_ci --ctx.state.recursionDepth; 165ffe3c632Sopenharmony_ci } 166ffe3c632Sopenharmony_ci 167ffe3c632Sopenharmony_ci public static void ReadRawMessage(ref ParseContext ctx, IMessage message) 168ffe3c632Sopenharmony_ci { 169ffe3c632Sopenharmony_ci if (message is IBufferMessage bufferMessage) 170ffe3c632Sopenharmony_ci { 171ffe3c632Sopenharmony_ci bufferMessage.InternalMergeFrom(ref ctx); 172ffe3c632Sopenharmony_ci } 173ffe3c632Sopenharmony_ci else 174ffe3c632Sopenharmony_ci { 175ffe3c632Sopenharmony_ci // If we reached here, it means we've ran into a nested message with older generated code 176ffe3c632Sopenharmony_ci // which doesn't provide the InternalMergeFrom method that takes a ParseContext. 177ffe3c632Sopenharmony_ci // With a slight performance overhead, we can still parse this message just fine, 178ffe3c632Sopenharmony_ci // but we need to find the original CodedInputStream instance that initiated this 179ffe3c632Sopenharmony_ci // parsing process and make sure its internal state is up to date. 180ffe3c632Sopenharmony_ci // Note that this performance overhead is not very high (basically copying contents of a struct) 181ffe3c632Sopenharmony_ci // and it will only be incurred in case the application mixes older and newer generated code. 182ffe3c632Sopenharmony_ci // Regenerating the code from .proto files will remove this overhead because it will 183ffe3c632Sopenharmony_ci // generate the InternalMergeFrom method we need. 184ffe3c632Sopenharmony_ci 185ffe3c632Sopenharmony_ci if (ctx.state.CodedInputStream == null) 186ffe3c632Sopenharmony_ci { 187ffe3c632Sopenharmony_ci // This can only happen when the parsing started without providing a CodedInputStream instance 188ffe3c632Sopenharmony_ci // (e.g. ParseContext was created directly from a ReadOnlySequence). 189ffe3c632Sopenharmony_ci // That also means that one of the new parsing APIs was used at the top level 190ffe3c632Sopenharmony_ci // and in such case it is reasonable to require that all the nested message provide 191ffe3c632Sopenharmony_ci // up-to-date generated code with ParseContext support (and fail otherwise). 192ffe3c632Sopenharmony_ci throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code."); 193ffe3c632Sopenharmony_ci } 194ffe3c632Sopenharmony_ci 195ffe3c632Sopenharmony_ci ctx.CopyStateTo(ctx.state.CodedInputStream); 196ffe3c632Sopenharmony_ci try 197ffe3c632Sopenharmony_ci { 198ffe3c632Sopenharmony_ci // fallback parse using the CodedInputStream that started current parsing tree 199ffe3c632Sopenharmony_ci message.MergeFrom(ctx.state.CodedInputStream); 200ffe3c632Sopenharmony_ci } 201ffe3c632Sopenharmony_ci finally 202ffe3c632Sopenharmony_ci { 203ffe3c632Sopenharmony_ci ctx.LoadStateFrom(ctx.state.CodedInputStream); 204ffe3c632Sopenharmony_ci } 205ffe3c632Sopenharmony_ci } 206ffe3c632Sopenharmony_ci } 207ffe3c632Sopenharmony_ci 208ffe3c632Sopenharmony_ci /// <summary> 209ffe3c632Sopenharmony_ci /// Verifies that the last call to ReadTag() returned tag 0 - in other words, 210ffe3c632Sopenharmony_ci /// we've reached the end of the stream when we expected to. 211ffe3c632Sopenharmony_ci /// </summary> 212ffe3c632Sopenharmony_ci /// <exception cref="InvalidProtocolBufferException">The 213ffe3c632Sopenharmony_ci /// tag read was not the one specified</exception> 214ffe3c632Sopenharmony_ci public static void CheckReadEndOfStreamTag(ref ParserInternalState state) 215ffe3c632Sopenharmony_ci { 216ffe3c632Sopenharmony_ci if (state.lastTag != 0) 217ffe3c632Sopenharmony_ci { 218ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.MoreDataAvailable(); 219ffe3c632Sopenharmony_ci } 220ffe3c632Sopenharmony_ci } 221ffe3c632Sopenharmony_ci 222ffe3c632Sopenharmony_ci private static void CheckLastTagWas(ref ParserInternalState state, uint expectedTag) 223ffe3c632Sopenharmony_ci { 224ffe3c632Sopenharmony_ci if (state.lastTag != expectedTag) { 225ffe3c632Sopenharmony_ci throw InvalidProtocolBufferException.InvalidEndTag(); 226ffe3c632Sopenharmony_ci } 227ffe3c632Sopenharmony_ci } 228ffe3c632Sopenharmony_ci } 229ffe3c632Sopenharmony_ci}