diff --git a/.github/actions/setup-dotnet/action.yaml b/.github/actions/setup-dotnet/action.yaml index e44880788..839f27ae0 100644 --- a/.github/actions/setup-dotnet/action.yaml +++ b/.github/actions/setup-dotnet/action.yaml @@ -14,7 +14,7 @@ runs: using: "composite" steps: # see: https://github.com/actions/setup-dotnet - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@v5 with: global-json-file: ${{ inputs.global-json-file }} diff --git a/.github/workflows/_create-release.yaml b/.github/workflows/_create-release.yaml index ec5728191..1c88829c7 100644 --- a/.github/workflows/_create-release.yaml +++ b/.github/workflows/_create-release.yaml @@ -42,10 +42,12 @@ on: jobs: create-release: name: Create Release + permissions: + id-token: write + contents: write env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # auto generated token GH_REPO: ${{ github.repository }} - NUGET_KEY: ${{ secrets.NUGET_KEY }} runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -56,14 +58,17 @@ jobs: echo "Validation error! 'inputs.release-asset-path' cannot be blank when 'inputs.release-upload' is true." exit 1 - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: ref: ${{ inputs.commit-id }} - uses: ./.github/actions/setup-dotnet # Download(All) Artifacts to $GITHUB_WORKSPACE - - name: donload artifacts - uses: actions/download-artifact@v4 # must sync with actions/upload-artifact@v4 in build-release + - name: download artifacts + uses: actions/download-artifact@v8 # must sync with actions/upload-artifact@v7 in build-release + with: + name: nuget + path: ./nuget - name: Show download aritifacts run: ls -lR - name: Validate package exists in artifact - release assets @@ -140,6 +145,13 @@ jobs: done <<< "${{ inputs.release-asset-path }}" if: ${{ inputs.release-upload }} + - name: NuGet login (OIDC) + id: nuget-login + if: ${{ inputs.nuget-push }} + uses: NuGet/login@v1 + with: + user: ${{ secrets.NUGET_USER }} + # Upload to NuGet - name: Upload to NuGet (DryRun=${{ inputs.dry-run }}) if: ${{ inputs.nuget-push }} @@ -153,9 +165,9 @@ jobs: fi if [[ "${{ inputs.dry-run }}" == "true" ]]; then - echo "(dry run) dotnet nuget push \"${nuget_path}\" --skip-duplicate -s https://api.nuget.org/v3/index.json -k \"${{ env.NUGET_KEY }}\"" + echo "(dry run) dotnet nuget push \"${nuget_path}\" --skip-duplicate -s https://api.nuget.org/v3/index.json -k \"***\"" else - dotnet nuget push "${nuget_path}" --skip-duplicate -s https://api.nuget.org/v3/index.json -k "${{ env.NUGET_KEY }}" + dotnet nuget push "${nuget_path}" --skip-duplicate -s https://api.nuget.org/v3/index.json -k "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" fi done <<< "${{ inputs.nuget-path }}" diff --git a/.github/workflows/_update-packagejson.yaml b/.github/workflows/_update-packagejson.yaml index c461c9bed..6b64f8e1b 100644 --- a/.github/workflows/_update-packagejson.yaml +++ b/.github/workflows/_update-packagejson.yaml @@ -71,7 +71,7 @@ jobs: run: | echo "branch-name=test-release/${{ inputs.tag }}" | tee -a "$GITHUB_OUTPUT" - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # package.json # "version": 1.2.3 -> "version": 2.0.0 diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 444bdae1a..b73370cf9 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -15,6 +15,7 @@ on: permissions: actions: write contents: write + id-token: write jobs: # for unity. need update package.json from tag @@ -34,7 +35,7 @@ jobs: timeout-minutes: 10 steps: - run: echo ${{ needs.update-packagejson.outputs.sha }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: ref: ${{ needs.update-packagejson.outputs.sha }} fetch-depth: 0 @@ -44,7 +45,7 @@ jobs: - run: dotnet test -c Release --no-build - run: dotnet pack -c Release -p:Version=${{ needs.update-packagejson.outputs.normalized_tag }} -o ./publish - name: upload artifacts - uses: actions/upload-artifact@v4 # must sync with actions/download-artifact@v4 in create-release + uses: actions/upload-artifact@v7 # must sync with actions/download-artifact@v8 in create-release with: name: nuget path: ./publish/ diff --git a/.gitignore b/.gitignore index 6ef3a7ca5..7209c6cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -371,3 +371,5 @@ src/MessagePack.UnityClient/Assets/Packages/ BenchmarkDotNet.Artifacts/ src/MessagePack.UnityClient/.vsconfig + +*.lscache diff --git a/LICENSE b/LICENSE index eebeb87e0..c4bc51dae 100644 --- a/LICENSE +++ b/LICENSE @@ -39,3 +39,21 @@ Redistributions of source code must retain the above copyright notice, this list Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +BufferWriter.cs + +Copyright 2019 .NET Foundation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/SECURITY.md b/SECURITY.md index aedee6a15..e2612cff1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,6 +12,12 @@ Each supported major version is only serviced for security issues at its tip. For example 2.5 will receive updates but 2.4 will not. 3.0 will receive updates until 3.1 is stable, at which point 3.0 will no longer received security updates. +## Strong-name key + +This repository intentionally includes `opensource.snk`, the strong-name key used to provide stable assembly identity for open-source builds. This key is public by design and is not a credential or package-authenticity secret. + +Do not rely on strong names as a security boundary or as proof that a package was published by the project maintainers. Consume packages from trusted package sources and use the normal NuGet and release provenance checks for package authenticity. + ## Reporting a Vulnerability Please use [the Security tab](https://github.com/MessagePack-CSharp/MessagePack-CSharp/security) to responsibly report security vulnerabilities. diff --git a/global.json b/global.json index 088f23e11..723fc16cb 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.300", "rollForward": "patch", "allowPrerelease": false } diff --git a/sandbox/Sandbox/Program.cs b/sandbox/Sandbox/Program.cs index d787a3247..f8f3ad819 100644 --- a/sandbox/Sandbox/Program.cs +++ b/sandbox/Sandbox/Program.cs @@ -26,7 +26,8 @@ //} -public class ClassA where T : ClassA.ClassB +public class ClassA + where T : ClassA.ClassB { public class ClassB { diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs index c2038f5a0..f84b4bf7a 100644 --- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs @@ -9,16 +9,17 @@ namespace MessagePack.AspNetCoreMvcFormatter public class MessagePackInputFormatter : InputFormatter { private const string ContentType = "application/x-msgpack"; - private readonly MessagePackSerializerOptions? options; + private static readonly MessagePackSerializerOptions DefaultOptions = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData); + private readonly MessagePackSerializerOptions options; public MessagePackInputFormatter() - : this(null) + : this(DefaultOptions) { } public MessagePackInputFormatter(MessagePackSerializerOptions? options) { - this.options = options; + this.options = options ?? DefaultOptions; SupportedMediaTypes.Add(ContentType); } diff --git a/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs index c35fe171a..959df2db8 100644 --- a/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs +++ b/src/MessagePack.SourceGenerator/CodeAnalysis/GenericTypeParameterInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) All contributors. All rights reserved. +// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; @@ -88,4 +88,8 @@ void AddIf(bool condition, string constraint) return builder.ToString(); } + + public virtual bool Equals(GenericTypeParameterInfo? other) => other is not null && this.Name == other.Name; + + public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(this.Name); } diff --git a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj index e3c4c5e90..5c73a4db2 100644 --- a/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj +++ b/src/MessagePack.SourceGenerator/MessagePack.SourceGenerator.csproj @@ -9,6 +9,7 @@ embedded false true + $(NoWarn);RS2007 diff --git a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs index 12ce6a290..08e2ed04a 100644 --- a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs +++ b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.cs @@ -71,7 +71,7 @@ public virtual string TransformText() if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair)) { writer.WriteArrayHeader(2); - writer.WriteInt32(keyValuePair.Key); + writer.Write(keyValuePair.Key); switch (keyValuePair.Value) { "); diff --git a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt index 67b78a537..359e165e7 100644 --- a/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt +++ b/src/MessagePack.SourceGenerator/Transforms/UnionTemplate.tt @@ -34,7 +34,7 @@ using MsgPack = global::MessagePack; if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair)) { writer.WriteArrayHeader(2); - writer.WriteInt32(keyValuePair.Key); + writer.Write(keyValuePair.Key); switch (keyValuePair.Value) { <# for(var i = 0; i < Info.SubTypes.Length; i++) { var item = Info.SubTypes[i]; #> diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Extension/UnsafeBlitFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Extension/UnsafeBlitFormatter.cs index 63f137a39..fdbe234fb 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Extension/UnsafeBlitFormatter.cs +++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Extension/UnsafeBlitFormatter.cs @@ -55,19 +55,25 @@ public unsafe void Serialize(ref MessagePackWriter writer, T[]? value, MessagePa return null; } - ExtensionHeader header = reader.ReadExtensionFormatHeader(); - if (header.TypeCode != this.TypeCode) + ExtensionResult extension = reader.ReadExtensionFormat(); + if (extension.TypeCode != this.TypeCode) { throw new InvalidOperationException("Invalid typeCode."); } - var byteLength = reader.ReadInt32(); - var isLittleEndian = reader.ReadBoolean(); + MessagePackReader extensionReader = reader.Clone(extension.Data); + var byteLength = extensionReader.ReadInt32(); + var isLittleEndian = extensionReader.ReadBoolean(); + long remainingBytes = extensionReader.Sequence.Length - extensionReader.Consumed; + if (byteLength < 0 || byteLength % sizeof(T) != 0 || byteLength != remainingBytes) + { + throw new MessagePackSerializationException("Invalid Unity blit extension length."); + } // Allocate a T[] that we will return. We'll then cast the T[] as byte[] so we can copy the byte sequence directly into it. var result = new T[byteLength / sizeof(T)]; Span resultAsBytes = MemoryMarshal.Cast(result); - reader.ReadRaw(byteLength).CopyTo(resultAsBytes); + extensionReader.ReadRaw(byteLength).CopyTo(resultAsBytes); // Reverse the byte order if necessary. if (isLittleEndian != BitConverter.IsLittleEndian && result.Length > 0) diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/package.json b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/package.json index 097e1d562..211ed594d 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/package.json +++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/package.json @@ -1,7 +1,7 @@ { "name": "com.github.messagepack-csharp", "displayName": "MessagePack", - "version": "3.1.4", + "version": "3.1.7", "unity": "2021.3", "description": "Extremely Fast MessagePack Serializer for C#.", "keywords": [ diff --git a/src/MessagePack/BufferWriter.cs b/src/MessagePack/BufferWriter.cs index 90ee06e3e..b2ca60937 100644 --- a/src/MessagePack/BufferWriter.cs +++ b/src/MessagePack/BufferWriter.cs @@ -2,7 +2,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) Andrew Arnott. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using System; using System.Buffers; diff --git a/src/MessagePack/Formatters/CollectionFormatter.cs b/src/MessagePack/Formatters/CollectionFormatter.cs index 2ed9bf563..cf1086b5b 100644 --- a/src/MessagePack/Formatters/CollectionFormatter.cs +++ b/src/MessagePack/Formatters/CollectionFormatter.cs @@ -928,7 +928,7 @@ protected override ILookup Complete(Dictionary> Create(int count, MessagePackSerializerOptions options) { - return new Dictionary>(count); + return new Dictionary>(count, options.Security.GetEqualityComparer()); } } diff --git a/src/MessagePack/Formatters/DictionaryFormatter.cs b/src/MessagePack/Formatters/DictionaryFormatter.cs index af4d367c5..32ebc2c6b 100644 --- a/src/MessagePack/Formatters/DictionaryFormatter.cs +++ b/src/MessagePack/Formatters/DictionaryFormatter.cs @@ -53,8 +53,7 @@ public void Serialize(ref MessagePackWriter writer, TDictionary? value, MessageP writer.WriteMapHeader(count); - TEnumerator e = this.GetSourceEnumerator(value); - try + using (TEnumerator e = this.GetSourceEnumerator(value)) { while (e.MoveNext()) { @@ -64,10 +63,6 @@ public void Serialize(ref MessagePackWriter writer, TDictionary? value, MessageP valueFormatter.Serialize(ref writer, item.Value, options); } } - finally - { - e.Dispose(); - } } } diff --git a/src/MessagePack/Formatters/DynamicObjectTypeFallbackFormatter.cs b/src/MessagePack/Formatters/DynamicObjectTypeFallbackFormatter.cs index 2c43fa7d2..884d391dd 100644 --- a/src/MessagePack/Formatters/DynamicObjectTypeFallbackFormatter.cs +++ b/src/MessagePack/Formatters/DynamicObjectTypeFallbackFormatter.cs @@ -33,7 +33,6 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe } Type type = value.GetType(); - TypeInfo ti = type.GetTypeInfo(); if (type == typeof(object)) { @@ -42,7 +41,7 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe return; } - if (PrimitiveObjectFormatter.IsSupportedType(type, ti, value)) + if (PrimitiveObjectFormatter.IsSupportedType(type, value)) { if (!(value is System.Collections.IDictionary || value is System.Collections.ICollection)) { @@ -70,7 +69,7 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe Expression.Convert(param0, formatterType), serializeMethodInfo, param1, - ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), + type.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3); serializerDelegate = Expression.Lambda(body, param0, param1, param2, param3).Compile(); diff --git a/src/MessagePack/Formatters/EnumAsStringFormatter`1.cs b/src/MessagePack/Formatters/EnumAsStringFormatter`1.cs index e3b81fd54..5458dbda4 100644 --- a/src/MessagePack/Formatters/EnumAsStringFormatter`1.cs +++ b/src/MessagePack/Formatters/EnumAsStringFormatter`1.cs @@ -39,8 +39,10 @@ public EnumAsStringFormatter(bool ignoreCase) this.ignoreCase = ignoreCase; StringComparer stringComparer = ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; - this.isFlags = typeof(T).GetCustomAttribute() is object; - var fields = typeof(T).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); + var type = typeof(T); + + this.isFlags = type.GetCustomAttribute() is object; + var fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); var nameValueMapping = new Dictionary(fields.Length, ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); var valueNameMapping = new Dictionary(); Dictionary? clrToSerializationName = null; diff --git a/src/MessagePack/Formatters/ExpandoObjectFormatter.cs b/src/MessagePack/Formatters/ExpandoObjectFormatter.cs index 2d9328880..98932401f 100644 --- a/src/MessagePack/Formatters/ExpandoObjectFormatter.cs +++ b/src/MessagePack/Formatters/ExpandoObjectFormatter.cs @@ -8,6 +8,8 @@ namespace MessagePack.Formatters { public class ExpandoObjectFormatter : IMessagePackFormatter { + internal const int MaximumUntrustedDataMemberCount = 1024; + public static readonly IMessagePackFormatter Instance = new ExpandoObjectFormatter(); private ExpandoObjectFormatter() @@ -23,6 +25,7 @@ private ExpandoObjectFormatter() var result = new ExpandoObject(); int count = reader.ReadMapHeader(); + ThrowIfMapTooLargeForUntrustedData(count, options); if (count > 0) { IFormatterResolver resolver = options.Resolver; @@ -49,6 +52,14 @@ private ExpandoObjectFormatter() return result; } + internal static void ThrowIfMapTooLargeForUntrustedData(int count, MessagePackSerializerOptions options) + { + if (options.Security.HashCollisionResistant && count > MaximumUntrustedDataMemberCount) + { + throw new MessagePackSerializationException($"ExpandoObject map size exceeds the limit of {MaximumUntrustedDataMemberCount} entries allowed under untrusted data security mode."); + } + } + public void Serialize(ref MessagePackWriter writer, ExpandoObject? value, MessagePackSerializerOptions options) { if (value is null) diff --git a/src/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs b/src/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs index 30b2f462b..4455b31e6 100644 --- a/src/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs +++ b/src/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs @@ -2,9 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Buffers; -using System.Collections.Generic; -using System.Text; +using System.Diagnostics.CodeAnalysis; using MessagePack.Internal; #pragma warning disable SA1402 // File may only contain a single type @@ -64,6 +62,7 @@ public void Serialize(ref MessagePackWriter writer, T[,]? value, MessagePackSeri var iLength = reader.ReadInt32(); var jLength = reader.ReadInt32(); var maxLen = reader.ReadArrayHeader(); + MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,]", maxLen, iLength, jLength); var array = new T[iLength, jLength]; @@ -151,6 +150,7 @@ public void Serialize(ref MessagePackWriter writer, T[,,]? value, MessagePackSer var jLength = reader.ReadInt32(); var kLength = reader.ReadInt32(); var maxLen = reader.ReadArrayHeader(); + MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,,]", maxLen, iLength, jLength, kLength); var array = new T[iLength, jLength, kLength]; @@ -248,6 +248,8 @@ public void Serialize(ref MessagePackWriter writer, T[,,,]? value, MessagePackSe var kLength = reader.ReadInt32(); var lLength = reader.ReadInt32(); var maxLen = reader.ReadArrayHeader(); + MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,,,]", maxLen, iLength, jLength, kLength, lLength); + var array = new T[iLength, jLength, kLength, lLength]; var i = 0; @@ -295,4 +297,34 @@ public void Serialize(ref MessagePackWriter writer, T[,,,]? value, MessagePackSe } } } + + internal static class MultiDimensionalArrayFormatterHelper + { + internal static void ThrowIfLengthsDontMatch(string format, int actualLength, int firstLength, int secondLength, int thirdLength = 1, int fourthLength = 1) + { + if (firstLength < 0 || secondLength < 0 || thirdLength < 0 || fourthLength < 0) + { + ThrowInvalidFormat(format); + } + + int expectedLength; + try + { + expectedLength = checked(firstLength * secondLength * thirdLength * fourthLength); + } + catch (OverflowException) + { + ThrowInvalidFormat(format); + return; + } + + if (expectedLength != actualLength) + { + ThrowInvalidFormat(format); + } + } + + [DoesNotReturn] + private static void ThrowInvalidFormat(string format) => throw new MessagePackSerializationException($"Invalid {format} format"); + } } diff --git a/src/MessagePack/Formatters/PrimitiveObjectFormatter.cs b/src/MessagePack/Formatters/PrimitiveObjectFormatter.cs index 0e3230e0d..ced58432c 100644 --- a/src/MessagePack/Formatters/PrimitiveObjectFormatter.cs +++ b/src/MessagePack/Formatters/PrimitiveObjectFormatter.cs @@ -37,7 +37,13 @@ protected PrimitiveObjectFormatter() { } + [Obsolete("Please, use the method overload without TypeInfo")] public static bool IsSupportedType(Type type, TypeInfo typeInfo, object value) + { + return IsSupportedType(type, value); + } + + public static bool IsSupportedType(Type type, object value) { if (value == null) { @@ -49,7 +55,7 @@ public static bool IsSupportedType(Type type, TypeInfo typeInfo, object value) return true; } - if (typeInfo.IsEnum) + if (type.IsEnum) { return true; } @@ -133,7 +139,7 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe } else { - if (t.GetTypeInfo().IsEnum) + if (t.IsEnum) { Type underlyingType = Enum.GetUnderlyingType(t); var code2 = TypeToJumpCode[underlyingType]; diff --git a/src/MessagePack/Formatters/StandardClassLibraryFormatter.cs b/src/MessagePack/Formatters/StandardClassLibraryFormatter.cs index 51ce1f8c1..0558394d8 100644 --- a/src/MessagePack/Formatters/StandardClassLibraryFormatter.cs +++ b/src/MessagePack/Formatters/StandardClassLibraryFormatter.cs @@ -257,6 +257,9 @@ private DateTimeOffsetFormatter() public void Serialize(ref MessagePackWriter writer, DateTimeOffset value, MessagePackSerializerOptions options) { writer.WriteArrayHeader(2); + + // We're writing a *local* DateTime value in msgpack encoding as if it were UTC time. + // That's incorrect msgpack encoding, but fixing it now would compromise backward compatibility. writer.Write(new DateTime(value.Ticks, DateTimeKind.Utc)); // current ticks as is writer.Write((short)value.Offset.TotalMinutes); // offset is normalized in minutes return; @@ -834,9 +837,14 @@ public void Serialize(ref MessagePackWriter writer, T? value, MessagePackSeriali public T? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { - return reader.ReadString() is string value - ? (T?)Type.GetType(value, throwOnError: true) - : null; + if (reader.ReadString() is not string value) + { + return null; + } + + Type type = options.LoadType(value) ?? throw new TypeLoadException(value); + options.ThrowIfDeserializingTypeIsDisallowed(type); + return (T?)type; } } diff --git a/src/MessagePack/Formatters/TypelessFormatter.cs b/src/MessagePack/Formatters/TypelessFormatter.cs index 9e63ca447..a051be19d 100644 --- a/src/MessagePack/Formatters/TypelessFormatter.cs +++ b/src/MessagePack/Formatters/TypelessFormatter.cs @@ -147,8 +147,7 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe var typeNameCache = options.OmitAssemblyVersion ? ShortenedTypeNameCache : FullTypeNameCache; if (!typeNameCache.TryGetValue(type, out byte[]? typeName)) { - TypeInfo ti = type.GetTypeInfo(); - if (ti.IsAnonymous() || UseBuiltinTypes.Contains(type)) + if (type.IsAnonymous() || UseBuiltinTypes.Contains(type)) { typeName = null; } @@ -176,8 +175,6 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe { if (!Serializers.TryGetValue(type, out serializeMethod)) { - TypeInfo ti = type.GetTypeInfo(); - Type formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type); ParameterExpression param0 = Expression.Parameter(typeof(object), "formatter"); ParameterExpression param1 = Expression.Parameter(typeof(MessagePackWriter).MakeByRefType(), "writer"); @@ -190,7 +187,7 @@ public void Serialize(ref MessagePackWriter writer, object? value, MessagePackSe Expression.Convert(param0, formatterType), serializeMethodInfo, param1, - ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), + type.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3); serializeMethod = Expression.Lambda(body, param0, param1, param2, param3).Compile(); @@ -305,8 +302,6 @@ private object DeserializeByTypeName(ArraySegment typeName, ref MessagePac { if (!Deserializers.TryGetValue(type, out deserializeMethod)) { - TypeInfo ti = type.GetTypeInfo(); - Type formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type); ParameterExpression param0 = Expression.Parameter(typeof(object), "formatter"); ParameterExpression param1 = Expression.Parameter(typeof(MessagePackReader).MakeByRefType(), "reader"); @@ -321,7 +316,7 @@ private object DeserializeByTypeName(ArraySegment typeName, ref MessagePac param2); Expression body = deserialize; - if (ti.IsValueType) + if (type.IsValueType) { body = Expression.Convert(deserialize, typeof(object)); } diff --git a/src/MessagePack/Formatters/UnsafeBinaryFormatters.cs b/src/MessagePack/Formatters/UnsafeBinaryFormatters.cs index 19147191c..df4a7c136 100644 --- a/src/MessagePack/Formatters/UnsafeBinaryFormatters.cs +++ b/src/MessagePack/Formatters/UnsafeBinaryFormatters.cs @@ -66,7 +66,7 @@ private NativeDecimalFormatter() { } - /* decimal underlying "flags, hi, lo, mid" fields are sequential and same layuout with .NET Framework and Mono(Unity) + /* decimal underlying "flags, hi, lo, mid" fields are sequential and same layout with .NET Framework and Mono(Unity) * But target machines must be same endian so restrict only for little endian. */ public unsafe void Serialize(ref MessagePackWriter writer, Decimal value, MessagePackSerializerOptions options) diff --git a/src/MessagePack/Internal/DynamicAssemblyFactory.cs b/src/MessagePack/Internal/DynamicAssemblyFactory.cs index e0804c69f..ea5c9d578 100644 --- a/src/MessagePack/Internal/DynamicAssemblyFactory.cs +++ b/src/MessagePack/Internal/DynamicAssemblyFactory.cs @@ -54,7 +54,7 @@ public DynamicAssemblyFactory(string moduleName) { ImmutableHashSet.Builder skipVisibilityAssemblies = this.lastCreatedDynamicAssemblySkipVisibilityChecks.ToBuilder(); int originalCount = skipVisibilityAssemblies.Count; - SkipClrVisibilityChecks.GetSkipVisibilityChecksRequirements(type.GetTypeInfo(), skipVisibilityAssemblies); + SkipClrVisibilityChecks.GetSkipVisibilityChecksRequirements(type, skipVisibilityAssemblies); lock (this) { diff --git a/src/MessagePack/Internal/ILGeneratorExtensions.cs b/src/MessagePack/Internal/ILGeneratorExtensions.cs index 43a969da9..ff088b895 100644 --- a/src/MessagePack/Internal/ILGeneratorExtensions.cs +++ b/src/MessagePack/Internal/ILGeneratorExtensions.cs @@ -27,8 +27,7 @@ public ArgumentField(ILGenerator il, int i, Type type) { this.il = il; this.i = i; - TypeInfo ti = type.GetTypeInfo(); - this.@ref = (ti.IsClass || ti.IsInterface || ti.IsAbstract) ? false : true; + this.@ref = (type.IsClass || type.IsInterface || type.IsAbstract) ? false : true; } public void EmitLoad() @@ -228,7 +227,7 @@ public static void EmitLdc_I4(this ILGenerator il, int value) public static void EmitUnboxOrCast(this ILGenerator il, Type type) { - if (type.GetTypeInfo().IsValueType) + if (type.IsValueType) { il.Emit(OpCodes.Unbox_Any, type); } @@ -240,7 +239,7 @@ public static void EmitUnboxOrCast(this ILGenerator il, Type type) public static void EmitBoxOrDoNothing(this ILGenerator il, Type type) { - if (type.GetTypeInfo().IsValueType) + if (type.IsValueType) { il.Emit(OpCodes.Box, type); } @@ -362,7 +361,8 @@ public static void EmitULong(this ILGenerator il, ulong value) public static void EmitThrowNotimplemented(this ILGenerator il) { - il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0)); + il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetConstructors() + .First(x => x.GetParameters().Length == 0)); il.Emit(OpCodes.Throw); } diff --git a/src/MessagePack/Internal/ReflectionExtensions.cs b/src/MessagePack/Internal/ReflectionExtensions.cs index 52eea0735..001fc9158 100644 --- a/src/MessagePack/Internal/ReflectionExtensions.cs +++ b/src/MessagePack/Internal/ReflectionExtensions.cs @@ -2,25 +2,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; namespace MessagePack.Internal { internal static class ReflectionExtensions { - public static bool IsNullable(this System.Reflection.TypeInfo type) + public static bool IsNullable(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); } - public static bool IsPublic(this System.Reflection.TypeInfo type) - { - return type.IsPublic; - } - - public static bool IsAnonymous(this System.Reflection.TypeInfo type) + public static bool IsAnonymous(this Type type) { return type.Namespace == null && type.IsSealed @@ -34,26 +27,5 @@ public static bool IsIndexer(this System.Reflection.PropertyInfo propertyInfo) { return propertyInfo.GetIndexParameters().Length > 0; } - - public static bool IsConstructedGenericType(this System.Reflection.TypeInfo type) - { - return type.AsType().IsConstructedGenericType; - } - - public static MethodInfo? GetGetMethod(this PropertyInfo propInfo) - { - return propInfo.GetMethod; - } - - public static MethodInfo? GetSetMethod(this PropertyInfo propInfo) - { - return propInfo.SetMethod; - } - - public static bool HasPrivateCtorForSerialization(this TypeInfo type) - { - var markedCtor = type.DeclaredConstructors.SingleOrDefault(x => x.GetCustomAttribute(false) != null); - return markedCtor?.Attributes.HasFlag(MethodAttributes.Private) ?? false; - } } } diff --git a/src/MessagePack/Internal/Sequence`1.cs b/src/MessagePack/Internal/Sequence`1.cs index 9d4ff862c..72a445541 100644 --- a/src/MessagePack/Internal/Sequence`1.cs +++ b/src/MessagePack/Internal/Sequence`1.cs @@ -28,7 +28,7 @@ namespace Nerdbank.Streams // NOTE: invalid namespace, should modify /// Instance members are not thread-safe. /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] - internal class Sequence : IBufferWriter, IDisposable + internal sealed class Sequence : IBufferWriter, IDisposable { private const int MaximumAutoGrowSize = 32 * 1024; @@ -361,7 +361,7 @@ private class SequenceSegment : ReadOnlySequenceSegment /// /// A value indicating whether the element may contain references (and thus must be cleared). /// - private static readonly bool MayContainReferences = !typeof(T).GetTypeInfo().IsPrimitive; + private static readonly bool MayContainReferences = !typeof(T).IsPrimitive; #pragma warning disable SA1011 // Closing square brackets should be spaced correctly /// diff --git a/src/MessagePack/Internal/TinyJsonReader.cs b/src/MessagePack/Internal/TinyJsonReader.cs index e4ceaabfe..67dbf01e8 100644 --- a/src/MessagePack/Internal/TinyJsonReader.cs +++ b/src/MessagePack/Internal/TinyJsonReader.cs @@ -58,7 +58,7 @@ protected TinyJsonException(SerializationInfo info, StreamingContext context) } } - internal class TinyJsonReader : IDisposable + internal sealed class TinyJsonReader : IDisposable { private readonly TextReader reader; private readonly bool disposeInnerReader; @@ -137,62 +137,64 @@ private static bool IsWordBreak(char c) private void ReadNextToken() { - this.SkipWhiteSpace(); - - var intChar = this.reader.Peek(); - if (intChar == -1) + while (true) { - this.TokenType = TinyJsonToken.None; - return; - } + this.SkipWhiteSpace(); - var c = (char)intChar; - switch (c) - { - case '{': - this.TokenType = TinyJsonToken.StartObject; - return; - case '}': - this.TokenType = TinyJsonToken.EndObject; - return; - case '[': - this.TokenType = TinyJsonToken.StartArray; - return; - case ']': - this.TokenType = TinyJsonToken.EndArray; - return; - case '"': - this.TokenType = TinyJsonToken.String; - return; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - this.TokenType = TinyJsonToken.Number; - return; - case 't': - this.TokenType = TinyJsonToken.True; - return; - case 'f': - this.TokenType = TinyJsonToken.False; - return; - case 'n': - this.TokenType = TinyJsonToken.Null; - return; - case ',': - case ':': - this.reader.Read(); - this.ReadNextToken(); + var intChar = this.reader.Peek(); + if (intChar == -1) + { + this.TokenType = TinyJsonToken.None; return; - default: - throw new TinyJsonException("Invalid String:" + c); + } + + var c = (char)intChar; + switch (c) + { + case '{': + this.TokenType = TinyJsonToken.StartObject; + return; + case '}': + this.TokenType = TinyJsonToken.EndObject; + return; + case '[': + this.TokenType = TinyJsonToken.StartArray; + return; + case ']': + this.TokenType = TinyJsonToken.EndArray; + return; + case '"': + this.TokenType = TinyJsonToken.String; + return; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + this.TokenType = TinyJsonToken.Number; + return; + case 't': + this.TokenType = TinyJsonToken.True; + return; + case 'f': + this.TokenType = TinyJsonToken.False; + return; + case 'n': + this.TokenType = TinyJsonToken.Null; + return; + case ',': + case ':': + this.reader.Read(); + continue; + default: + throw new TinyJsonException("Invalid String:" + c); + } } } diff --git a/src/MessagePack/Internal/UnsafeMemory.Low.cs b/src/MessagePack/Internal/UnsafeMemory.Low.cs index 00683ea89..08224a7a5 100644 --- a/src/MessagePack/Internal/UnsafeMemory.Low.cs +++ b/src/MessagePack/Internal/UnsafeMemory.Low.cs @@ -15,12 +15,20 @@ public static class UnsafeMemory public static readonly bool Is32Bit = IntPtr.Size == 4; } + /// + /// Highly tuned method for writing raw bytes to a . + /// + /// + /// The methods on this class are not safe, in that they use pointer arithmetic + /// and assume that the caller has provided a with a length + /// of at least the number of bytes being written. The caller must ensure that this is the case. + /// public static partial class UnsafeMemory32 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(1); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -28,13 +36,13 @@ public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(2); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -42,13 +50,13 @@ public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(3); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -57,16 +65,24 @@ public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan + /// Highly tuned method for writing raw bytes to a . + /// + /// + /// The methods on this class are not safe, in that they use pointer arithmetic + /// and assume that the caller has provided a with a length + /// of at least the number of bytes being written. The caller must ensure that this is the case. + /// public static partial class UnsafeMemory64 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(1); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -74,13 +90,13 @@ public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(2); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -88,13 +104,13 @@ public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(3); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -103,13 +119,13 @@ public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(4); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -117,13 +133,13 @@ public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(5); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -132,13 +148,13 @@ public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(6); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -147,13 +163,13 @@ public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(7); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -162,7 +178,7 @@ public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(4); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -26,13 +26,13 @@ public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(5); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -41,13 +41,13 @@ public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(6); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -56,13 +56,13 @@ public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(7); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -71,13 +71,13 @@ public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(8); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -86,13 +86,13 @@ public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(9); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -102,13 +102,13 @@ public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(10); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -118,13 +118,13 @@ public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 6) = *(int*)(pSrc + 6); } - writer.Advance(src.Length); + writer.Advance(10); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(11); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -134,13 +134,13 @@ public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 7) = *(int*)(pSrc + 7); } - writer.Advance(src.Length); + writer.Advance(11); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(12); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -150,13 +150,13 @@ public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 8) = *(int*)(pSrc + 8); } - writer.Advance(src.Length); + writer.Advance(12); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(13); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -167,13 +167,13 @@ public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 9) = *(int*)(pSrc + 9); } - writer.Advance(src.Length); + writer.Advance(13); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(14); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -184,13 +184,13 @@ public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 10) = *(int*)(pSrc + 10); } - writer.Advance(src.Length); + writer.Advance(14); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(15); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -201,13 +201,13 @@ public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 11) = *(int*)(pSrc + 11); } - writer.Advance(src.Length); + writer.Advance(15); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(16); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -218,13 +218,13 @@ public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 12) = *(int*)(pSrc + 12); } - writer.Advance(src.Length); + writer.Advance(16); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(17); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -236,13 +236,13 @@ public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 13) = *(int*)(pSrc + 13); } - writer.Advance(src.Length); + writer.Advance(17); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(18); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -254,13 +254,13 @@ public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 14) = *(int*)(pSrc + 14); } - writer.Advance(src.Length); + writer.Advance(18); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(19); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -272,13 +272,13 @@ public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 15) = *(int*)(pSrc + 15); } - writer.Advance(src.Length); + writer.Advance(19); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(20); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -290,13 +290,13 @@ public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 16) = *(int*)(pSrc + 16); } - writer.Advance(src.Length); + writer.Advance(20); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(21); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -309,13 +309,13 @@ public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 17) = *(int*)(pSrc + 17); } - writer.Advance(src.Length); + writer.Advance(21); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(22); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -328,13 +328,13 @@ public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 18) = *(int*)(pSrc + 18); } - writer.Advance(src.Length); + writer.Advance(22); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(23); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -347,13 +347,13 @@ public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 19) = *(int*)(pSrc + 19); } - writer.Advance(src.Length); + writer.Advance(23); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(24); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -366,13 +366,13 @@ public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 20) = *(int*)(pSrc + 20); } - writer.Advance(src.Length); + writer.Advance(24); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(25); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -386,13 +386,13 @@ public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 21) = *(int*)(pSrc + 21); } - writer.Advance(src.Length); + writer.Advance(25); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(26); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -406,13 +406,13 @@ public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 22) = *(int*)(pSrc + 22); } - writer.Advance(src.Length); + writer.Advance(26); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(27); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -426,13 +426,13 @@ public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 23) = *(int*)(pSrc + 23); } - writer.Advance(src.Length); + writer.Advance(27); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(28); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -446,13 +446,13 @@ public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 24) = *(int*)(pSrc + 24); } - writer.Advance(src.Length); + writer.Advance(28); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(29); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -467,13 +467,13 @@ public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 25) = *(int*)(pSrc + 25); } - writer.Advance(src.Length); + writer.Advance(29); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(30); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -488,13 +488,13 @@ public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 26) = *(int*)(pSrc + 26); } - writer.Advance(src.Length); + writer.Advance(30); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(31); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -509,7 +509,7 @@ public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan< *(int*)(pDst + 27) = *(int*)(pSrc + 27); } - writer.Advance(src.Length); + writer.Advance(31); } } @@ -518,7 +518,7 @@ public static partial class UnsafeMemory64 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(8); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -526,13 +526,13 @@ public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(9); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -541,13 +541,13 @@ public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(10); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -556,13 +556,13 @@ public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 2) = *(long*)(pSrc + 2); } - writer.Advance(src.Length); + writer.Advance(10); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(11); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -571,13 +571,13 @@ public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 3) = *(long*)(pSrc + 3); } - writer.Advance(src.Length); + writer.Advance(11); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(12); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -586,13 +586,13 @@ public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 4) = *(long*)(pSrc + 4); } - writer.Advance(src.Length); + writer.Advance(12); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(13); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -601,13 +601,13 @@ public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 5) = *(long*)(pSrc + 5); } - writer.Advance(src.Length); + writer.Advance(13); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(14); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -616,13 +616,13 @@ public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 6) = *(long*)(pSrc + 6); } - writer.Advance(src.Length); + writer.Advance(14); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(15); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -631,13 +631,13 @@ public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 7) = *(long*)(pSrc + 7); } - writer.Advance(src.Length); + writer.Advance(15); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(16); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -646,13 +646,13 @@ public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 8) = *(long*)(pSrc + 8); } - writer.Advance(src.Length); + writer.Advance(16); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(17); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -662,13 +662,13 @@ public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 9) = *(long*)(pSrc + 9); } - writer.Advance(src.Length); + writer.Advance(17); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(18); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -678,13 +678,13 @@ public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 10) = *(long*)(pSrc + 10); } - writer.Advance(src.Length); + writer.Advance(18); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(19); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -694,13 +694,13 @@ public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 11) = *(long*)(pSrc + 11); } - writer.Advance(src.Length); + writer.Advance(19); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(20); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -710,13 +710,13 @@ public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 12) = *(long*)(pSrc + 12); } - writer.Advance(src.Length); + writer.Advance(20); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(21); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -726,13 +726,13 @@ public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 13) = *(long*)(pSrc + 13); } - writer.Advance(src.Length); + writer.Advance(21); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(22); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -742,13 +742,13 @@ public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 14) = *(long*)(pSrc + 14); } - writer.Advance(src.Length); + writer.Advance(22); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(23); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -758,13 +758,13 @@ public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 15) = *(long*)(pSrc + 15); } - writer.Advance(src.Length); + writer.Advance(23); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(24); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -774,13 +774,13 @@ public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 16) = *(long*)(pSrc + 16); } - writer.Advance(src.Length); + writer.Advance(24); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(25); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -791,13 +791,13 @@ public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 17) = *(long*)(pSrc + 17); } - writer.Advance(src.Length); + writer.Advance(25); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(26); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -808,13 +808,13 @@ public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 18) = *(long*)(pSrc + 18); } - writer.Advance(src.Length); + writer.Advance(26); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(27); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -825,13 +825,13 @@ public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 19) = *(long*)(pSrc + 19); } - writer.Advance(src.Length); + writer.Advance(27); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(28); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -842,13 +842,13 @@ public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 20) = *(long*)(pSrc + 20); } - writer.Advance(src.Length); + writer.Advance(28); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(29); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -859,13 +859,13 @@ public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 21) = *(long*)(pSrc + 21); } - writer.Advance(src.Length); + writer.Advance(29); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(30); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -876,13 +876,13 @@ public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 22) = *(long*)(pSrc + 22); } - writer.Advance(src.Length); + writer.Advance(30); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(31); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -893,7 +893,7 @@ public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan< *(long*)(pDst + 23) = *(long*)(pSrc + 23); } - writer.Advance(src.Length); + writer.Advance(31); } } } diff --git a/src/MessagePack/Internal/UnsafeMemory.tt b/src/MessagePack/Internal/UnsafeMemory.tt index 3410e207b..3610b7c9e 100644 --- a/src/MessagePack/Internal/UnsafeMemory.tt +++ b/src/MessagePack/Internal/UnsafeMemory.tt @@ -28,7 +28,7 @@ namespace MessagePack.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw<#= i #>(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(<#= i #>); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -41,7 +41,7 @@ namespace MessagePack.Internal <# } #> } - writer.Advance(src.Length); + writer.Advance(<#= i #>); } <# } #> } @@ -52,7 +52,7 @@ namespace MessagePack.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteRaw<#= i #>(ref MessagePackWriter writer, ReadOnlySpan src) { - Span dst = writer.GetSpan(src.Length); + Span dst = writer.GetSpan(<#= i #>); fixed (byte* pSrc = &src[0]) fixed (byte* pDst = &dst[0]) @@ -65,7 +65,7 @@ namespace MessagePack.Internal <# } #> } - writer.Advance(src.Length); + writer.Advance(<#= i #>); } <# } #> } diff --git a/src/MessagePack/LZ4/LZ4Codec.Unsafe.cs b/src/MessagePack/LZ4/LZ4Codec.Unsafe.cs index ec9a28bc9..1efabf184 100644 --- a/src/MessagePack/LZ4/LZ4Codec.Unsafe.cs +++ b/src/MessagePack/LZ4/LZ4Codec.Unsafe.cs @@ -99,11 +99,11 @@ public static unsafe int Decode(ReadOnlySpan input, Span output) int length; if (IntPtr.Size == 4) { - length = LZ4_uncompress_32(inputPtr, outputPtr, output.Length); + length = LZ4_uncompress_32(inputPtr, input.Length, outputPtr, output.Length); } else { - length = LZ4_uncompress_64(inputPtr, outputPtr, output.Length); + length = LZ4_uncompress_64(inputPtr, input.Length, outputPtr, output.Length); } if (length != input.Length) diff --git a/src/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs b/src/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs index 22d39b25d..23a40ddca 100644 --- a/src/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs +++ b/src/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs @@ -600,6 +600,7 @@ private static unsafe int LZ4_compress64kCtx_32( private static unsafe int LZ4_uncompress_32( byte* src, + int src_len, byte* dst, int dst_len) { @@ -609,6 +610,7 @@ private static unsafe int LZ4_uncompress_32( { // r93 var src_p = src; + var src_end = src + src_len; byte* xxx_ref; var dst_p = dst; @@ -627,16 +629,26 @@ private static unsafe int LZ4_uncompress_32( int length; // get runlength + if (src_p >= src_end) + { + goto _output_error; + } + xxx_token = *src_p++; if ((length = (int)(xxx_token >> ML_BITS)) == RUN_MASK) { int len; - for (; (len = *src_p++) == 255; length += 255) + do { - /* do nothing */ - } + if (src_p >= src_end) + { + goto _output_error; + } - length += len; + len = *src_p++; + length += len; + } + while (len == 255); } // copy literals @@ -649,11 +661,21 @@ private static unsafe int LZ4_uncompress_32( goto _output_error; // Error : not enough place for another match (min 4) + 5 literals } + if (length > src_end - src_p) + { + goto _output_error; + } + BlockCopy32(src_p, dst_p, length); src_p += length; break; // EOF } + if (length > src_end - src_p) + { + goto _output_error; + } + do { *(uint*)dst_p = *(uint*)src_p; @@ -668,6 +690,11 @@ private static unsafe int LZ4_uncompress_32( dst_p = dst_cpy; // get offset + if (src_end - src_p < 2) + { + goto _output_error; + } + xxx_ref = dst_cpy - (*(ushort*)src_p); src_p += 2; if (xxx_ref < dst) @@ -678,12 +705,18 @@ private static unsafe int LZ4_uncompress_32( // get matchlength if ((length = (int)(xxx_token & ML_MASK)) == ML_MASK) { - for (; *src_p == 255; length += 255) + int len; + do { - src_p++; - } + if (src_p >= src_end) + { + goto _output_error; + } - length += *src_p++; + len = *src_p++; + length += len; + } + while (len == 255); } // copy repeated sequence diff --git a/src/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs b/src/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs index ba10e68d2..f807ef17e 100644 --- a/src/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs +++ b/src/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs @@ -612,6 +612,7 @@ private static unsafe int LZ4_compress64kCtx_64( private static unsafe int LZ4_uncompress_64( byte* src, + int src_len, byte* dst, int dst_len) { @@ -622,6 +623,7 @@ private static unsafe int LZ4_uncompress_64( { // r93 var src_p = src; + var src_end = src + src_len; byte* dst_ref; var dst_p = dst; @@ -640,16 +642,26 @@ private static unsafe int LZ4_uncompress_64( int length; // get runlength + if (src_p >= src_end) + { + goto _output_error; + } + token = *src_p++; if ((length = token >> ML_BITS) == RUN_MASK) { int len; - for (; (len = *src_p++) == 255; length += 255) + do { - /* do nothing */ - } + if (src_p >= src_end) + { + goto _output_error; + } - length += len; + len = *src_p++; + length += len; + } + while (len == 255); } // copy literals @@ -662,11 +674,21 @@ private static unsafe int LZ4_uncompress_64( goto _output_error; // Error : not enough place for another match (min 4) + 5 literals } + if (length > src_end - src_p) + { + goto _output_error; + } + BlockCopy64(src_p, dst_p, length); src_p += length; break; // EOF } + if (length > src_end - src_p) + { + goto _output_error; + } + do { *(ulong*)dst_p = *(ulong*)src_p; @@ -678,6 +700,11 @@ private static unsafe int LZ4_uncompress_64( dst_p = dst_cpy; // get offset + if (src_end - src_p < 2) + { + goto _output_error; + } + dst_ref = dst_cpy - (*(ushort*)src_p); src_p += 2; if (dst_ref < dst) @@ -688,12 +715,18 @@ private static unsafe int LZ4_uncompress_64( // get matchlength if ((length = token & ML_MASK) == ML_MASK) { - for (; *src_p == 255; length += 255) + int len; + do { - src_p++; - } + if (src_p >= src_end) + { + goto _output_error; + } - length += *src_p++; + len = *src_p++; + length += len; + } + while (len == 255); } // copy repeated sequence diff --git a/src/MessagePack/MessagePackPrimitives.Readers.cs b/src/MessagePack/MessagePackPrimitives.Readers.cs index e7da34e48..1de3a33d5 100644 --- a/src/MessagePack/MessagePackPrimitives.Readers.cs +++ b/src/MessagePack/MessagePackPrimitives.Readers.cs @@ -342,6 +342,12 @@ public static DecodeResult TryReadDateTime(ReadOnlySpan source, ExtensionH return DecodeResult.TokenMismatch; } + if (header.Length is not (4 or 8 or 12)) + { + value = default; + return DecodeResult.TokenMismatch; + } + if (source.Length < tokenSize) { value = default; diff --git a/src/MessagePack/MessagePackReader.cs b/src/MessagePack/MessagePackReader.cs index b7eebae74..351921abf 100644 --- a/src/MessagePack/MessagePackReader.cs +++ b/src/MessagePack/MessagePackReader.cs @@ -153,64 +153,123 @@ public byte NextCode /// internal bool TrySkip() { - if (this.reader.Remaining == 0) + long remainingStructures = 1; + while (remainingStructures > 0) { - return false; - } + if (this.reader.Remaining == 0) + { + return false; + } - byte code = this.NextCode; - switch (code) - { - case byte x when MessagePackCode.IsPositiveFixInt(x) || MessagePackCode.IsNegativeFixInt(x): - case MessagePackCode.Nil: - case MessagePackCode.True: - case MessagePackCode.False: - return this.reader.TryAdvance(1); - case MessagePackCode.Int8: - case MessagePackCode.UInt8: - return this.reader.TryAdvance(2); - case MessagePackCode.Int16: - case MessagePackCode.UInt16: - return this.reader.TryAdvance(3); - case MessagePackCode.Int32: - case MessagePackCode.UInt32: - case MessagePackCode.Float32: - return this.reader.TryAdvance(5); - case MessagePackCode.Int64: - case MessagePackCode.UInt64: - case MessagePackCode.Float64: - return this.reader.TryAdvance(9); - case byte x when MessagePackCode.IsFixMap(x): - case MessagePackCode.Map16: - case MessagePackCode.Map32: - return this.TrySkipNextMap(); - case byte x when MessagePackCode.IsFixArray(x): - case MessagePackCode.Array16: - case MessagePackCode.Array32: - return this.TrySkipNextArray(); - case byte x when MessagePackCode.IsFixStr(x): - case MessagePackCode.Str8: - case MessagePackCode.Str16: - case MessagePackCode.Str32: - return this.TryGetStringLengthInBytes(out uint length) && this.reader.TryAdvance(length); - case MessagePackCode.Bin8: - case MessagePackCode.Bin16: - case MessagePackCode.Bin32: - return this.TryGetBytesLength(out length) && this.reader.TryAdvance(length); - case MessagePackCode.FixExt1: - case MessagePackCode.FixExt2: - case MessagePackCode.FixExt4: - case MessagePackCode.FixExt8: - case MessagePackCode.FixExt16: - case MessagePackCode.Ext8: - case MessagePackCode.Ext16: - case MessagePackCode.Ext32: - return this.TryReadExtensionFormatHeader(out ExtensionHeader header) && this.reader.TryAdvance(header.Length); - default: - // We don't actually expect to ever hit this point, since every code is supported. - Debug.Fail("Missing handler for code: " + code); - throw ThrowInvalidCode(code); + remainingStructures--; + byte code = this.NextCode; + switch (code) + { + case byte x when MessagePackCode.IsPositiveFixInt(x) || MessagePackCode.IsNegativeFixInt(x): + case MessagePackCode.Nil: + case MessagePackCode.True: + case MessagePackCode.False: + if (!this.reader.TryAdvance(1)) + { + return false; + } + + break; + case MessagePackCode.Int8: + case MessagePackCode.UInt8: + if (!this.reader.TryAdvance(2)) + { + return false; + } + + break; + case MessagePackCode.Int16: + case MessagePackCode.UInt16: + if (!this.reader.TryAdvance(3)) + { + return false; + } + + break; + case MessagePackCode.Int32: + case MessagePackCode.UInt32: + case MessagePackCode.Float32: + if (!this.reader.TryAdvance(5)) + { + return false; + } + + break; + case MessagePackCode.Int64: + case MessagePackCode.UInt64: + case MessagePackCode.Float64: + if (!this.reader.TryAdvance(9)) + { + return false; + } + + break; + case byte x when MessagePackCode.IsFixMap(x): + case MessagePackCode.Map16: + case MessagePackCode.Map32: + if (!this.TryReadMapHeader(out int count)) + { + return false; + } + + remainingStructures = checked(remainingStructures + ((long)count * 2)); + break; + case byte x when MessagePackCode.IsFixArray(x): + case MessagePackCode.Array16: + case MessagePackCode.Array32: + if (!this.TryReadArrayHeader(out count)) + { + return false; + } + + remainingStructures = checked(remainingStructures + count); + break; + case byte x when MessagePackCode.IsFixStr(x): + case MessagePackCode.Str8: + case MessagePackCode.Str16: + case MessagePackCode.Str32: + if (!this.TryGetStringLengthInBytes(out uint length) || !this.reader.TryAdvance(length)) + { + return false; + } + + break; + case MessagePackCode.Bin8: + case MessagePackCode.Bin16: + case MessagePackCode.Bin32: + if (!this.TryGetBytesLength(out length) || !this.reader.TryAdvance(length)) + { + return false; + } + + break; + case MessagePackCode.FixExt1: + case MessagePackCode.FixExt2: + case MessagePackCode.FixExt4: + case MessagePackCode.FixExt8: + case MessagePackCode.FixExt16: + case MessagePackCode.Ext8: + case MessagePackCode.Ext16: + case MessagePackCode.Ext32: + if (!this.TryReadExtensionFormatHeader(out ExtensionHeader header) || !this.reader.TryAdvance(header.Length)) + { + return false; + } + + break; + default: + // We don't actually expect to ever hit this point, since every code is supported. + Debug.Fail("Missing handler for code: " + code); + throw ThrowInvalidCode(code); + } } + + return true; } /// @@ -371,7 +430,7 @@ public int ReadMapHeader() // Protect against corrupted or mischievous data that may lead to allocating way too much memory. // We allow for each primitive to be the minimal 1 byte in size, and we have a key=value map, so that's 2 bytes. // Formatters that know each element is larger can optionally add a stronger check. - ThrowInsufficientBufferUnless(this.reader.Remaining >= count * 2); + ThrowInsufficientBufferUnless(this.reader.Remaining >= (long)count * 2); return count; } @@ -1099,21 +1158,5 @@ private string ReadStringSlow(uint byteLength) return value; } - private bool TrySkipNextArray() => this.TryReadArrayHeader(out int count) && this.TrySkip(count); - - private bool TrySkipNextMap() => this.TryReadMapHeader(out int count) && this.TrySkip(count * 2); - - private bool TrySkip(int count) - { - for (int i = 0; i < count; i++) - { - if (!this.TrySkip()) - { - return false; - } - } - - return true; - } } } diff --git a/src/MessagePack/MessagePackSecurity.cs b/src/MessagePack/MessagePackSecurity.cs index 2e59d957f..515d8a024 100644 --- a/src/MessagePack/MessagePackSecurity.cs +++ b/src/MessagePack/MessagePackSecurity.cs @@ -19,6 +19,8 @@ namespace MessagePack /// public class MessagePackSecurity { + private const int DefaultUntrustedDataMaximumDecompressedSize = 64 * 1024 * 1024; + /// /// Gets an instance preconfigured with settings that omit hash collision resistance protections. /// Useful for deserializing fully-trusted and valid msgpack sequences. @@ -27,6 +29,7 @@ public class MessagePackSecurity { HashCollisionResistant = false, MaximumObjectGraphDepth = 500, + MaximumDecompressedSize = int.MaxValue, }; /// @@ -36,6 +39,7 @@ public class MessagePackSecurity { HashCollisionResistant = true, MaximumObjectGraphDepth = 500, + MaximumDecompressedSize = DefaultUntrustedDataMaximumDecompressedSize, }; private static readonly SipHash Hash = new(); @@ -62,6 +66,7 @@ protected MessagePackSecurity(MessagePackSecurity copyFrom) this.HashCollisionResistant = copyFrom.HashCollisionResistant; this.MaximumObjectGraphDepth = copyFrom.MaximumObjectGraphDepth; + this.MaximumDecompressedSize = copyFrom.MaximumDecompressedSize; } /// @@ -86,6 +91,12 @@ protected MessagePackSecurity(MessagePackSecurity copyFrom) /// public int MaximumObjectGraphDepth { get; private set; } = 500; + /// + /// Gets the maximum decompressed size in bytes allowed when deserializing compressed payloads. + /// + /// The default value is for and 64MB for . + public int MaximumDecompressedSize { get; private set; } = int.MaxValue; + /// /// Gets a copy of these options with the property set to a new value. /// @@ -103,6 +114,28 @@ public MessagePackSecurity WithMaximumObjectGraphDepth(int maximumObjectGraphDep return clone; } + /// + /// Gets a copy of these options with the property set to a new value. + /// + /// The new value for the property. Must not be negative. + /// The new instance; or the original if the value is unchanged. + public MessagePackSecurity WithMaximumDecompressedSize(int maximumDecompressedSize) + { + if (this.MaximumDecompressedSize == maximumDecompressedSize) + { + return this; + } + + if (maximumDecompressedSize < 0) + { + throw new ArgumentOutOfRangeException(nameof(maximumDecompressedSize)); + } + + var clone = this.Clone(); + clone.MaximumDecompressedSize = maximumDecompressedSize; + return clone; + } + /// /// Gets a copy of these options with the property set to a new value. /// @@ -153,30 +186,32 @@ private class HashResistantCache static HashResistantCache() { + var type = typeof(T); + // We have to specially handle some 32-bit types (e.g. float) where multiple in-memory representations should hash to the same value. // Any type supported by the PrimitiveObjectFormatter should be added here if supporting it as a key in a collection makes sense. EqualityComparer = - typeof(T) == typeof(bool) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(char) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(sbyte) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(byte) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(short) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(ushort) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(int) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(uint) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(long) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(ulong) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : - typeof(T) == typeof(Guid) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(bool) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(char) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(sbyte) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(byte) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(short) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(ushort) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(int) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(uint) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(long) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(ulong) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : + type == typeof(Guid) ? (IEqualityComparer)CollisionResistantHasherUnmanaged.Instance : // Data types that are managed or have multiple in-memory representations for equivalent values: - typeof(T) == typeof(float) ? (IEqualityComparer)SingleEqualityComparer.Instance : - typeof(T) == typeof(double) ? (IEqualityComparer)DoubleEqualityComparer.Instance : - typeof(T) == typeof(string) ? (IEqualityComparer)StringEqualityComparer.Instance : - typeof(T) == typeof(DateTime) ? (IEqualityComparer)DateTimeEqualityComparer.Instance : - typeof(T) == typeof(DateTimeOffset) ? (IEqualityComparer)DateTimeOffsetEqualityComparer.Instance : + type == typeof(float) ? (IEqualityComparer)SingleEqualityComparer.Instance : + type == typeof(double) ? (IEqualityComparer)DoubleEqualityComparer.Instance : + type == typeof(string) ? (IEqualityComparer)StringEqualityComparer.Instance : + type == typeof(DateTime) ? (IEqualityComparer)DateTimeEqualityComparer.Instance : + type == typeof(DateTimeOffset) ? (IEqualityComparer)DateTimeOffsetEqualityComparer.Instance : // Call out each primitive behind an enum explicitly to avoid dynamically generating code. - typeof(T).GetTypeInfo().IsEnum && typeof(T).GetTypeInfo().GetEnumUnderlyingType() is Type underlying ? ( + type.IsEnum && type.GetEnumUnderlyingType() is Type underlying ? ( underlying == typeof(byte) ? CollisionResistantEnumHasher.Instance : underlying == typeof(sbyte) ? CollisionResistantEnumHasher.Instance : underlying == typeof(ushort) ? CollisionResistantEnumHasher.Instance : diff --git a/src/MessagePack/MessagePackSerializer.Json.cs b/src/MessagePack/MessagePackSerializer.Json.cs index 1dfdf49db..055783a82 100644 --- a/src/MessagePack/MessagePackSerializer.Json.cs +++ b/src/MessagePack/MessagePackSerializer.Json.cs @@ -44,9 +44,11 @@ public static void SerializeToJson(TextWriter textWriter, T obj, MessagePackS /// Thrown if an error occurs during serialization. public static string SerializeToJson(T obj, MessagePackSerializerOptions? options = null, CancellationToken cancellationToken = default) { - var writer = new StringWriter(); - SerializeToJson(writer, obj, options, cancellationToken); - return writer.ToString(); + using (var writer = new StringWriter()) + { + SerializeToJson(writer, obj, options, cancellationToken); + return writer.ToString(); + } } /// @@ -61,13 +63,15 @@ public static string SerializeToJson(T obj, MessagePackSerializerOptions? opt /// Thrown if an error occurs while reading the messagepack data or writing out the JSON. public static string ConvertToJson(in ReadOnlySequence bytes, MessagePackSerializerOptions? options = null, CancellationToken cancellationToken = default) { - var jsonWriter = new StringWriter(); - var reader = new MessagePackReader(bytes) + using (var jsonWriter = new StringWriter()) { - CancellationToken = cancellationToken, - }; - ConvertToJson(ref reader, jsonWriter, options); - return jsonWriter.ToString(); + var reader = new MessagePackReader(bytes) + { + CancellationToken = cancellationToken, + }; + ConvertToJson(ref reader, jsonWriter, options); + return jsonWriter.ToString(); + } } /// @@ -88,7 +92,7 @@ public static void ConvertToJson(ref MessagePackReader reader, TextWriter jsonWr { using (var scratchRental = options.SequencePool.Rent()) { - if (TryDecompress(ref reader, scratchRental.Value)) + if (TryDecompress(ref reader, scratchRental.Value, options)) { var scratchReader = new MessagePackReader(scratchRental.Value) { @@ -183,6 +187,11 @@ public static void ConvertFromJson(TextReader reader, ref MessagePackWriter writ } private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer, MessagePackSerializerOptions options) + { + return FromJsonCore(jr, ref writer, options, 0); + } + + private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer, MessagePackSerializerOptions options, int depth) { uint count = 0; while (jr.Read()) @@ -192,11 +201,13 @@ private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer case TinyJsonToken.None: break; case TinyJsonToken.StartObject: + VerifyJsonObjectGraphDepth(options, depth); + // Set up a scratch area to serialize the collection since we don't know its length yet, which must be written first. using (var scratchRental = options.SequencePool.Rent()) { MessagePackWriter scratchWriter = writer.Clone(scratchRental.Value); - var mapCount = FromJsonCore(jr, ref scratchWriter, options); + var mapCount = FromJsonCore(jr, ref scratchWriter, options, depth + 1); scratchWriter.Flush(); mapCount = mapCount / 2; // remove propertyname string count. @@ -209,11 +220,13 @@ private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer case TinyJsonToken.EndObject: return count; // break case TinyJsonToken.StartArray: + VerifyJsonObjectGraphDepth(options, depth); + // Set up a scratch area to serialize the collection since we don't know its length yet, which must be written first. using (var scratchRental = options.SequencePool.Rent()) { MessagePackWriter scratchWriter = writer.Clone(scratchRental.Value); - var arrayCount = FromJsonCore(jr, ref scratchWriter, options); + var arrayCount = FromJsonCore(jr, ref scratchWriter, options, depth + 1); scratchWriter.Flush(); writer.WriteArrayHeader(arrayCount); @@ -269,6 +282,14 @@ private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer return count; } + private static void VerifyJsonObjectGraphDepth(MessagePackSerializerOptions options, int depth) + { + if (depth >= options.Security.MaximumObjectGraphDepth) + { + throw new InsufficientExecutionStackException($"This JSON sequence has an object graph that exceeds the maximum depth allowed of {options.Security.MaximumObjectGraphDepth}."); + } + } + private static void ToJsonCore(ref MessagePackReader reader, TextWriter writer, MessagePackSerializerOptions options) { MessagePackType type = reader.NextMessagePackType; @@ -391,46 +412,54 @@ private static void ToJsonCore(ref MessagePackReader reader, TextWriter writer, } else if (extHeader.TypeCode == ReservedExtensionTypeCodes.TypelessFormatter) { - // prepare type name token - var privateBuilder = new StringBuilder(); - var typeNameTokenBuilder = new StringBuilder(); - SequencePosition positionBeforeTypeNameRead = reader.Position; - ToJsonCore(ref reader, new StringWriter(typeNameTokenBuilder), options); - int typeNameReadSize = (int)reader.Sequence.Slice(positionBeforeTypeNameRead, reader.Position).Length; - if (extHeader.Length > typeNameReadSize) + options.Security.DepthStep(ref reader); + try { - // object map or array - MessagePackType typeInside = reader.NextMessagePackType; - if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) + // prepare type name token + var privateBuilder = new StringBuilder(); + var typeNameTokenBuilder = new StringBuilder(); + SequencePosition positionBeforeTypeNameRead = reader.Position; + ToJsonCore(ref reader, new StringWriter(typeNameTokenBuilder), options); + int typeNameReadSize = (int)reader.Sequence.Slice(positionBeforeTypeNameRead, reader.Position).Length; + if (extHeader.Length > typeNameReadSize) { - privateBuilder.Append("{"); - } + // object map or array + MessagePackType typeInside = reader.NextMessagePackType; + if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) + { + privateBuilder.Append("{"); + } - ToJsonCore(ref reader, new StringWriter(privateBuilder), options); + ToJsonCore(ref reader, new StringWriter(privateBuilder), options); - // insert type name token to start of object map or array - if (typeInside != MessagePackType.Array) - { - typeNameTokenBuilder.Insert(0, "\"$type\":"); - } + // insert type name token to start of object map or array + if (typeInside != MessagePackType.Array) + { + typeNameTokenBuilder.Insert(0, "\"$type\":"); + } - if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) - { - privateBuilder.Append("}"); - } + if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) + { + privateBuilder.Append("}"); + } - if (privateBuilder.Length > 2) - { - typeNameTokenBuilder.Append(","); - } + if (privateBuilder.Length > 2) + { + typeNameTokenBuilder.Append(","); + } - privateBuilder.Insert(1, typeNameTokenBuilder.ToString()); + privateBuilder.Insert(1, typeNameTokenBuilder.ToString()); - writer.Write(privateBuilder.ToString()); + writer.Write(privateBuilder.ToString()); + } + else + { + writer.Write("{\"$type\":" + typeNameTokenBuilder.ToString() + "}"); + } } - else + finally { - writer.Write("{\"$type\":" + typeNameTokenBuilder.ToString() + "}"); + reader.Depth--; } } else diff --git a/src/MessagePack/MessagePackSerializer.NonGeneric.cs b/src/MessagePack/MessagePackSerializer.NonGeneric.cs index 1495e4e65..b86c4c862 100644 --- a/src/MessagePack/MessagePackSerializer.NonGeneric.cs +++ b/src/MessagePack/MessagePackSerializer.NonGeneric.cs @@ -134,7 +134,6 @@ private class CompiledMethods internal CompiledMethods(Type type) { - TypeInfo ti = type.GetTypeInfo(); { // public static byte[] Serialize(T obj, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo serialize = GetMethod(nameof(Serialize), type, new Type?[] { null, typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); @@ -151,7 +150,7 @@ internal CompiledMethods(Type type) MethodCallExpression body = Expression.Call( null, serialize, - ti.IsValueType ? Expression.Unbox(param1, type) : Expression.Convert(param1, type), + type.IsValueType ? Expression.Unbox(param1, type) : Expression.Convert(param1, type), param2, param3); Func lambda = Expression.Lambda>(body, param1, param2, param3).Compile(PreferInterpretation); @@ -178,7 +177,7 @@ internal CompiledMethods(Type type) null, serialize, param1, - ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), + type.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Action lambda = Expression.Lambda>(body, param1, param2, param3, param4).Compile(PreferInterpretation); @@ -205,7 +204,7 @@ internal CompiledMethods(Type type) null, serialize, param1, - ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), + type.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Func lambda = Expression.Lambda>(body, param1, param2, param3, param4).Compile(PreferInterpretation); @@ -232,7 +231,7 @@ internal CompiledMethods(Type type) null, serialize, param1, - ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), + type.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Action, object?, MessagePackSerializerOptions?, CancellationToken> lambda = Expression.Lambda, object?, MessagePackSerializerOptions?, CancellationToken>>(body, param1, param2, param3, param4).Compile(PreferInterpretation); diff --git a/src/MessagePack/MessagePackSerializer.cs b/src/MessagePack/MessagePackSerializer.cs index 3cbb44cec..98764cde6 100644 --- a/src/MessagePack/MessagePackSerializer.cs +++ b/src/MessagePack/MessagePackSerializer.cs @@ -233,7 +233,7 @@ public static T Deserialize(ref MessagePackReader reader, MessagePackSerializ using (var msgPackUncompressedRental = options.SequencePool.Rent()) { var msgPackUncompressed = msgPackUncompressedRental.Value; - if (TryDecompress(ref reader, msgPackUncompressed)) + if (TryDecompress(ref reader, msgPackUncompressed, options)) { MessagePackReader uncompressedReader = reader.Clone(msgPackUncompressed.AsReadOnlySequence); return options.Resolver.GetFormatterWithVerify().Deserialize(ref uncompressedReader, options); @@ -481,7 +481,7 @@ private static int LZ4Operation(in ReadOnlySequence input, Span outp } } - private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter writer) + private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter writer, MessagePackSerializerOptions options) { if (!reader.End) { @@ -503,6 +503,7 @@ private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter compressedData = extReader.Sequence.Slice(extReader.Position); + ThrowIfInvalidLz4BlockLength(uncompressedLength, options.Security.MaximumDecompressedSize); Span uncompressedSpan = writer.GetSpan(uncompressedLength).Slice(0, uncompressedLength); int actualUncompressedLength = LZ4Operation(compressedData, uncompressedSpan, LZ4CodecDecode); Debug.Assert(actualUncompressedLength == uncompressedLength, "Unexpected length of uncompressed data."); @@ -529,6 +530,7 @@ private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter.Shared.Rent(sequenceCount); try { + long remainingMaxDecompressedSize = options.Security.MaximumDecompressedSize; for (int i = 0; i < sequenceCount; i++) { uncompressedLengths[i] = reader.ReadInt32(); @@ -538,6 +540,8 @@ private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter lz4Block = reader.ReadBytes() ?? throw MessagePackSerializationException.ThrowUnexpectedNilWhileDeserializing>(); + ThrowIfInvalidLz4BlockLength(uncompressedLength, remainingMaxDecompressedSize); + remainingMaxDecompressedSize -= uncompressedLength; Span uncompressedSpan = writer.GetSpan(uncompressedLength).Slice(0, uncompressedLength); var actualUncompressedLength = LZ4Operation(lz4Block, uncompressedSpan, LZ4CodecDecode); Debug.Assert(actualUncompressedLength == uncompressedLength, "Unexpected length of uncompressed data."); @@ -558,6 +562,14 @@ private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter remainingMaxDecompressedSize) + { + throw new MessagePackSerializationException("LZ4 block declares a decompressed length that exceeds the configured maximum."); + } + } + private static void ToLZ4BinaryCore(in ReadOnlySequence msgpackUncompressedData, ref MessagePackWriter writer, MessagePackCompression compression, int minCompressionSize) { if (msgpackUncompressedData.Length < minCompressionSize) diff --git a/src/MessagePack/MessagePackSerializerOptions.cs b/src/MessagePack/MessagePackSerializerOptions.cs index f750f93df..8c660f464 100644 --- a/src/MessagePack/MessagePackSerializerOptions.cs +++ b/src/MessagePack/MessagePackSerializerOptions.cs @@ -24,8 +24,25 @@ public class MessagePackSerializerOptions /// private static readonly HashSet DisallowedTypes = new HashSet { + "Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties", + "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.TempFileCollection", + "System.Configuration.SettingsPropertyValue", + "System.Data.DataSet", + "System.Data.DataTable", + "System.Diagnostics.Process", + "System.Diagnostics.ProcessStartInfo", + "System.Drawing.Design.ToolboxItemContainer", + "System.IdentityModel.Tokens.SessionSecurityToken", "System.Management.IWbemClassObjectFreeThreaded", + "System.Security.Claims.ClaimsIdentity", + "System.Security.Claims.ClaimsPrincipal", + "System.Security.Principal.WindowsIdentity", + "System.Security.Principal.WindowsPrincipal", + "System.Web.Security.RolePrincipal", + "System.Windows.Data.ObjectDataProvider", + "System.Windows.ResourceDictionary", + "System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector", }; #if !DYNAMICCODEDUMPER @@ -169,16 +186,32 @@ protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom) /// The type to be instantiated. /// Thrown if the is not allowed to be deserialized. /// + /// /// This method provides a means for an important security mitigation when using the Typeless formatter to prevent untrusted messagepack from /// deserializing objects that may be harmful if instantiated, disposed or finalized. - /// The default implementation throws for only a few known dangerous types. + /// The default implementation throws for only a few known dangerous types, or types that nest those dangerous types as generic type arguments or array element types. /// Applications that deserialize from untrusted sources should override this method and throw if the type is not among the expected set. + /// + /// + /// This method is for backward compatibility reasons. + /// For better security, the preferred method to override is . + /// /// public virtual void ThrowIfDeserializingTypeIsDisallowed(Type type) { - if (type.FullName is string fullName && DisallowedTypes.Contains(fullName)) + this.ThrowIfDeserializingTypeIsDisallowedCore(type); + + if (type.HasElementType && type.GetElementType() is Type elementType) { - throw new MessagePackSerializationException($"Deserialization attempted to create the type {fullName} which is not allowed."); + this.ThrowIfDeserializingTypeIsDisallowed(elementType); + } + + if (type.IsConstructedGenericType) + { + foreach (Type genericTypeArgument in type.GenericTypeArguments) + { + this.ThrowIfDeserializingTypeIsDisallowed(genericTypeArgument); + } } } @@ -355,6 +388,31 @@ public MessagePackSerializerOptions WithPool(SequencePool pool) return result; } + /// + /// Checks whether a specific given type may be deserialized, disregarding generic type arguments or array element types. + /// + /// The type to be instantiated. + /// Thrown if the is not allowed to be deserialized. + /// + /// + /// This method provides a means for an important security mitigation when using the Typeless formatter to prevent untrusted messagepack from + /// deserializing objects that may be harmful if instantiated, disposed or finalized. + /// The default implementation throws for only a few known dangerous types. + /// Applications that deserialize from untrusted sources should override this method and throw if the type is not among the expected set. + /// + /// + /// This method is called from the default implementation of + /// for the top-level type and again for each generic type argument or array element type. + /// + /// + protected virtual void ThrowIfDeserializingTypeIsDisallowedCore(Type type) + { + if (type.FullName is string fullName && DisallowedTypes.Contains(fullName)) + { + throw new MessagePackSerializationException($"Deserialization attempted to create the type {fullName} which is not allowed."); + } + } + /// /// Creates a clone of this instance with the same properties set. /// diff --git a/src/MessagePack/MessagePackWriter.cs b/src/MessagePack/MessagePackWriter.cs index e8ef3f068..b3b78f5aa 100644 --- a/src/MessagePack/MessagePackWriter.cs +++ b/src/MessagePack/MessagePackWriter.cs @@ -456,7 +456,7 @@ public void Write(byte[]? src) /// /// When is , the msgpack code used is , or instead. /// - public void Write(ReadOnlySpan src) + public void Write(scoped ReadOnlySpan src) { int length = (int)src.Length; this.WriteBinHeader(length); @@ -607,7 +607,7 @@ public unsafe void Write(string? value) /// . /// /// The value to write. - public unsafe void Write(ReadOnlySpan value) + public unsafe void Write(scoped ReadOnlySpan value) { ref byte buffer = ref this.WriteString_PrepareSpan(value.Length, out int bufferSize, out int useOffset); fixed (char* pValue = value) diff --git a/src/MessagePack/Resolvers/AttributeFormatterResolver.cs b/src/MessagePack/Resolvers/AttributeFormatterResolver.cs index eec41fc92..e420661ac 100644 --- a/src/MessagePack/Resolvers/AttributeFormatterResolver.cs +++ b/src/MessagePack/Resolvers/AttributeFormatterResolver.cs @@ -33,7 +33,7 @@ private static class FormatterCache static FormatterCache() { - MessagePackFormatterAttribute? attr = typeof(T).GetTypeInfo().GetCustomAttribute(); + MessagePackFormatterAttribute? attr = typeof(T).GetCustomAttribute(); if (attr == null) { return; diff --git a/src/MessagePack/Resolvers/DynamicEnumAsStringIgnoreCaseResolver.cs b/src/MessagePack/Resolvers/DynamicEnumAsStringIgnoreCaseResolver.cs index afa565d02..2a7f723c2 100644 --- a/src/MessagePack/Resolvers/DynamicEnumAsStringIgnoreCaseResolver.cs +++ b/src/MessagePack/Resolvers/DynamicEnumAsStringIgnoreCaseResolver.cs @@ -32,27 +32,27 @@ private static class FormatterCache static FormatterCache() { - TypeInfo ti = typeof(T).GetTypeInfo(); + Type type = typeof(T); - if (ti.IsNullable()) + if (type.IsNullable()) { // build underlying type and use wrapped formatter. - ti = ti.GenericTypeArguments[0].GetTypeInfo(); - if (!ti.IsEnum) + type = type.GenericTypeArguments[0]; + if (!type.IsEnum) { return; } - var innerFormatter = Instance.GetFormatterDynamic(ti.AsType()); + var innerFormatter = Instance.GetFormatterDynamic(type); if (innerFormatter == null) { return; } - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), new object[] { innerFormatter }); return; } - else if (!ti.IsEnum) + else if (!type.IsEnum) { return; } diff --git a/src/MessagePack/Resolvers/DynamicEnumAsStringResolver.cs b/src/MessagePack/Resolvers/DynamicEnumAsStringResolver.cs index b8610fa3c..d2de589e2 100644 --- a/src/MessagePack/Resolvers/DynamicEnumAsStringResolver.cs +++ b/src/MessagePack/Resolvers/DynamicEnumAsStringResolver.cs @@ -41,27 +41,27 @@ private static class FormatterCache static FormatterCache() { - TypeInfo ti = typeof(T).GetTypeInfo(); + Type type = typeof(T); - if (ti.IsNullable()) + if (type.IsNullable()) { // build underlying type and use wrapped formatter. - ti = ti.GenericTypeArguments[0].GetTypeInfo(); - if (!ti.IsEnum) + type = type.GenericTypeArguments[0]; + if (!type.IsEnum) { return; } - var innerFormatter = DynamicEnumAsStringResolver.Instance.GetFormatterDynamic(ti.AsType()); + var innerFormatter = DynamicEnumAsStringResolver.Instance.GetFormatterDynamic(type); if (innerFormatter == null) { return; } - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), new object[] { innerFormatter }); return; } - else if (!ti.IsEnum) + else if (!type.IsEnum) { return; } diff --git a/src/MessagePack/Resolvers/DynamicEnumResolver.cs b/src/MessagePack/Resolvers/DynamicEnumResolver.cs index c05769f1b..e993f4af0 100644 --- a/src/MessagePack/Resolvers/DynamicEnumResolver.cs +++ b/src/MessagePack/Resolvers/DynamicEnumResolver.cs @@ -54,32 +54,32 @@ private static class FormatterCache static FormatterCache() { - TypeInfo ti = typeof(T).GetTypeInfo(); - if (ti.IsNullable()) + Type type = typeof(T); + if (type.IsNullable()) { // build underlying type and use wrapped formatter. - ti = ti.GenericTypeArguments[0].GetTypeInfo(); - if (!ti.IsEnum) + type = type.GenericTypeArguments[0]; + if (!type.IsEnum) { return; } - var innerFormatter = DynamicEnumResolver.Instance.GetFormatterDynamic(ti.AsType()); + var innerFormatter = DynamicEnumResolver.Instance.GetFormatterDynamic(type); if (innerFormatter == null) { return; } - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), new object[] { innerFormatter }); return; } - else if (!ti.IsEnum) + else if (!type.IsEnum) { return; } - TypeInfo formatterTypeInfo = BuildType(typeof(T), allowPrivate: false); - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(formatterTypeInfo.AsType()); + TypeInfo formatterTypeInfo = BuildType(type, allowPrivate: false); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(formatterTypeInfo); } } diff --git a/src/MessagePack/Resolvers/DynamicGenericResolver.cs b/src/MessagePack/Resolvers/DynamicGenericResolver.cs index cb1a2248a..31a5a73fe 100644 --- a/src/MessagePack/Resolvers/DynamicGenericResolver.cs +++ b/src/MessagePack/Resolvers/DynamicGenericResolver.cs @@ -93,8 +93,6 @@ internal static class DynamicGenericResolverGetFormatterHelper // Reduce IL2CPP code generate size(don't write long code in ) internal static object? GetFormatter(Type t) { - TypeInfo ti = t.GetTypeInfo(); - if (t.IsArray) { var rank = t.GetArrayRank(); @@ -125,23 +123,22 @@ internal static class DynamicGenericResolverGetFormatterHelper return null; // not supported built-in } } - else if (ti.IsGenericType) + else if (t.IsGenericType) { - Type genericType = ti.GetGenericTypeDefinition(); - TypeInfo genericTypeInfo = genericType.GetTypeInfo(); - var isNullable = genericTypeInfo.IsNullable(); - Type? nullableElementType = isNullable ? ti.GenericTypeArguments[0] : null; + Type genericType = t.GetGenericTypeDefinition(); + var isNullable = genericType.IsNullable(); + Type? nullableElementType = isNullable ? t.GenericTypeArguments[0] : null; if (genericType == typeof(KeyValuePair<,>)) { - return CreateInstance(typeof(KeyValuePairFormatter<,>), ti.GenericTypeArguments); + return CreateInstance(typeof(KeyValuePairFormatter<,>), t.GenericTypeArguments); } // Tuple - else if (ti.FullName?.StartsWith("System.Tuple") is true) + else if (t.FullName?.StartsWith("System.Tuple") is true) { Type? tupleFormatterType = null; - switch (ti.GenericTypeArguments.Length) + switch (t.GenericTypeArguments.Length) { case 1: tupleFormatterType = typeof(TupleFormatter<>); @@ -168,17 +165,17 @@ internal static class DynamicGenericResolverGetFormatterHelper tupleFormatterType = typeof(TupleFormatter<,,,,,,,>); break; default: - throw new MessagePackSerializationException("Unsupported arity for Tuple generic type: " + ti.Name); + throw new MessagePackSerializationException("Unsupported arity for Tuple generic type: " + t.Name); } - return CreateInstance(tupleFormatterType, ti.GenericTypeArguments); + return CreateInstance(tupleFormatterType, t.GenericTypeArguments); } // ValueTuple - else if (ti.FullName?.StartsWith("System.ValueTuple") is true) + else if (t.FullName?.StartsWith("System.ValueTuple") is true) { Type? tupleFormatterType = null; - switch (ti.GenericTypeArguments.Length) + switch (t.GenericTypeArguments.Length) { case 1: tupleFormatterType = typeof(ValueTupleFormatter<>); @@ -205,61 +202,61 @@ internal static class DynamicGenericResolverGetFormatterHelper tupleFormatterType = typeof(ValueTupleFormatter<,,,,,,,>); break; default: - throw new MessagePackSerializationException("Unsupported arity for ValueTuple generic type: " + ti.Name); + throw new MessagePackSerializationException("Unsupported arity for ValueTuple generic type: " + t.Name); } - return CreateInstance(tupleFormatterType, ti.GenericTypeArguments); + return CreateInstance(tupleFormatterType, t.GenericTypeArguments); } // ArraySegment else if (genericType == typeof(ArraySegment<>)) { - if (ti.GenericTypeArguments[0] == typeof(byte)) + if (t.GenericTypeArguments[0] == typeof(byte)) { return ByteArraySegmentFormatter.Instance; } else { - return CreateInstance(typeof(ArraySegmentFormatter<>), ti.GenericTypeArguments); + return CreateInstance(typeof(ArraySegmentFormatter<>), t.GenericTypeArguments); } } // Memory else if (genericType == typeof(Memory<>)) { - if (ti.GenericTypeArguments[0] == typeof(byte)) + if (t.GenericTypeArguments[0] == typeof(byte)) { return ByteMemoryFormatter.Instance; } else { - return CreateInstance(typeof(MemoryFormatter<>), ti.GenericTypeArguments); + return CreateInstance(typeof(MemoryFormatter<>), t.GenericTypeArguments); } } // ReadOnlyMemory else if (genericType == typeof(ReadOnlyMemory<>)) { - if (ti.GenericTypeArguments[0] == typeof(byte)) + if (t.GenericTypeArguments[0] == typeof(byte)) { return ByteReadOnlyMemoryFormatter.Instance; } else { - return CreateInstance(typeof(ReadOnlyMemoryFormatter<>), ti.GenericTypeArguments); + return CreateInstance(typeof(ReadOnlyMemoryFormatter<>), t.GenericTypeArguments); } } // ReadOnlySequence else if (genericType == typeof(ReadOnlySequence<>)) { - if (ti.GenericTypeArguments[0] == typeof(byte)) + if (t.GenericTypeArguments[0] == typeof(byte)) { return ByteReadOnlySequenceFormatter.Instance; } else { - return CreateInstance(typeof(ReadOnlySequenceFormatter<>), ti.GenericTypeArguments); + return CreateInstance(typeof(ReadOnlySequenceFormatter<>), t.GenericTypeArguments); } } @@ -274,11 +271,11 @@ internal static class DynamicGenericResolverGetFormatterHelper { if (FormatterMap.TryGetValue(genericType, out Type? formatterType)) { - return CreateInstance(formatterType, ti.GenericTypeArguments); + return CreateInstance(formatterType, t.GenericTypeArguments); } } } - else if (ti.IsEnum) + else if (t.IsEnum) { return CreateInstance(typeof(GenericEnumFormatter<>), new[] { t }); } @@ -302,11 +299,13 @@ internal static class DynamicGenericResolverGetFormatterHelper return NonGenericInterfaceDictionaryFormatter.Instance; } - if (typeof(IList).GetTypeInfo().IsAssignableFrom(ti) && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) + if (typeof(IList).IsAssignableFrom(t) && t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic) + .Any(x => x.GetParameters().Length == 0)) { return Activator.CreateInstance(typeof(NonGenericListFormatter<>).MakeGenericType(t)); } - else if (typeof(IDictionary).GetTypeInfo().IsAssignableFrom(ti) && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) + else if (typeof(IDictionary).IsAssignableFrom(t) && t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic) + .Any(x => x.GetParameters().Length == 0)) { return Activator.CreateInstance(typeof(NonGenericDictionaryFormatter<>).MakeGenericType(t)); } @@ -315,8 +314,9 @@ internal static class DynamicGenericResolverGetFormatterHelper // check inherited types(e.g. Foo : ICollection<>, Bar : ICollection) { // generic dictionary - var dictionaryDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)); - if (dictionaryDef != null && ti.DeclaredConstructors.Any(x => !x.IsStatic && x.GetParameters().Length == 0)) + var dictionaryDef = t.GetInterfaces().FirstOrDefault(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)); + if (dictionaryDef != null && t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic) + .Any(x => x.GetParameters().Length == 0)) { Type keyType = dictionaryDef.GenericTypeArguments[0]; Type valueType = dictionaryDef.GenericTypeArguments[1]; @@ -324,7 +324,7 @@ internal static class DynamicGenericResolverGetFormatterHelper } // generic dictionary with collection ctor - var dictionaryInterfaceDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && + var dictionaryInterfaceDef = t.GetInterfaces().FirstOrDefault(x => x.IsConstructedGenericType && (x.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))); if (dictionaryInterfaceDef != null) { @@ -336,7 +336,7 @@ internal static class DynamicGenericResolverGetFormatterHelper typeof(IReadOnlyDictionary<,>).MakeGenericType(keyType, valueType), typeof(IEnumerable<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType)), }; - foreach (var constructor in ti.DeclaredConstructors) + foreach (var constructor in t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic)) { ParameterInfo[] parameters = constructor.GetParameters(); if (parameters.Length == 1 && @@ -348,8 +348,9 @@ internal static class DynamicGenericResolverGetFormatterHelper } // generic collection - var collectionDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(ICollection<>)); - if (collectionDef != null && ti.DeclaredConstructors.Any(x => !x.IsStatic && x.GetParameters().Length == 0)) + var collectionDef = t.GetInterfaces().FirstOrDefault(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)); + if (collectionDef != null && t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic) + .Any(x => x.GetParameters().Length == 0)) { Type elemType = collectionDef.GenericTypeArguments[0]; return CreateInstance(typeof(GenericCollectionFormatter<,>), new[] { elemType, t }); @@ -358,11 +359,11 @@ internal static class DynamicGenericResolverGetFormatterHelper // generic IEnumerable collection // looking for combination of IEnumerable and constructor that takes // enumeration of the same type - foreach (var enumerableCollectionDef in ti.ImplementedInterfaces.Where(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) + foreach (var enumerableCollectionDef in t.GetInterfaces().Where(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { Type elemType = enumerableCollectionDef.GenericTypeArguments[0]; Type paramInterface = typeof(IEnumerable<>).MakeGenericType(elemType); - foreach (var constructor in ti.DeclaredConstructors) + foreach (var constructor in t.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic)) { var parameters = constructor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(paramInterface)) diff --git a/src/MessagePack/Resolvers/DynamicObjectResolver.cs b/src/MessagePack/Resolvers/DynamicObjectResolver.cs index c3d281337..6eb62e235 100644 --- a/src/MessagePack/Resolvers/DynamicObjectResolver.cs +++ b/src/MessagePack/Resolvers/DynamicObjectResolver.cs @@ -61,15 +61,15 @@ private DynamicObjectResolver() internal static IMessagePackFormatter? BuildFormatterHelper(IFormatterResolver self, DynamicAssemblyFactory dynamicAssemblyFactory, bool forceStringKey, bool contractless, bool allowPrivate) { - TypeInfo ti = typeof(T).GetTypeInfo(); + Type type = typeof(T); - if (ti.IsInterface || ti.IsAbstract) + if (type.IsInterface || type.IsAbstract) { return null; } DynamicAssembly? dynamicAssembly = null; - if (ti.IsAnonymous()) + if (type.IsAnonymous()) { forceStringKey = true; contractless = true; @@ -78,25 +78,25 @@ private DynamicObjectResolver() // but *not* look at non-public members to avoid double-serialization of the properties // as well as their backing fields. allowPrivate = false; - dynamicAssembly = DynamicAssemblyFactory.GetDynamicAssembly(typeof(T), true); + dynamicAssembly = DynamicAssemblyFactory.GetDynamicAssembly(type, true); } - else if (ti.IsNullable()) + else if (type.IsNullable()) { - ti = ti.GenericTypeArguments[0].GetTypeInfo(); + type = type.GenericTypeArguments[0]; - var innerFormatter = self.GetFormatterDynamic(ti.AsType()); + var innerFormatter = self.GetFormatterDynamic(type); if (innerFormatter == null) { return null; } - return (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), [innerFormatter]); + return (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), [innerFormatter]); } - allowPrivate |= !contractless && typeof(T).GetCustomAttributes().Any(a => a.AllowPrivate); - dynamicAssembly ??= DynamicAssemblyFactory.GetDynamicAssembly(typeof(T), allowPrivate); - TypeInfo? formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(dynamicAssembly, typeof(T), forceStringKey, contractless, allowPrivate); - return formatterTypeInfo is null ? null : (IMessagePackFormatter)ResolverUtilities.ActivateFormatter(formatterTypeInfo.AsType()); + allowPrivate |= !contractless && type.GetCustomAttributes().Any(a => a.AllowPrivate); + dynamicAssembly ??= DynamicAssemblyFactory.GetDynamicAssembly(type, allowPrivate); + TypeInfo? formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(dynamicAssembly, type, forceStringKey, contractless, allowPrivate); + return formatterTypeInfo is null ? null : (IMessagePackFormatter)ResolverUtilities.ActivateFormatter(formatterTypeInfo); } private static class FormatterCache @@ -239,7 +239,7 @@ internal static class DynamicObjectTypeBuilder return null; } - if (!allowPrivate && !(type.IsPublic || type.IsNestedPublic) && !type.GetTypeInfo().IsAnonymous()) + if (!allowPrivate && !(type.IsPublic || type.IsNestedPublic) && !type.IsAnonymous()) { throw new MessagePackSerializationException("Building dynamic formatter only allows public type. Type: " + type.FullName); } @@ -462,7 +462,7 @@ private static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGe var argOptions = new ArgumentField(il, firstArgIndex + 2); // if(value == null) return WriteNil - if (type.GetTypeInfo().IsClass) + if (type.IsClass) { Label elseBody = il.DefineLabel(); @@ -476,7 +476,7 @@ private static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGe } // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize() - if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) + if (type.GetInterfaces().Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) { // call directly MethodInfo[] runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray(); @@ -515,7 +515,7 @@ private static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGe { if (intKeyMap.TryGetValue(i, out ObjectSerializationInfo.EmittableMember? member)) { - EmitSerializeValue(il, type.GetTypeInfo(), member, index++, tryEmitLoadCustomFormatter, argWriter, argValue, argOptions, localResolver); + EmitSerializeValue(il, member, index++, tryEmitLoadCustomFormatter, argWriter, argValue, argOptions, localResolver); } else { @@ -568,7 +568,7 @@ private static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGe il.EmitCall(MessagePackWriterTypeInfo.WriteRaw); } - EmitSerializeValue(il, type.GetTypeInfo(), item, index, tryEmitLoadCustomFormatter, argWriter, argValue, argOptions, localResolver); + EmitSerializeValue(il, item, index, tryEmitLoadCustomFormatter, argWriter, argValue, argOptions, localResolver); index++; } } @@ -576,7 +576,7 @@ private static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGe il.Emit(OpCodes.Ret); } - private static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSerializationInfo.EmittableMember member, int index, Func tryEmitLoadCustomFormatter, ArgumentField argWriter, ArgumentField argValue, ArgumentField argOptions, LocalBuilder localResolver) + private static void EmitSerializeValue(ILGenerator il, ObjectSerializationInfo.EmittableMember member, int index, Func tryEmitLoadCustomFormatter, ArgumentField argWriter, ArgumentField argValue, ArgumentField argOptions, LocalBuilder localResolver) { Label endLabel = il.DefineLabel(); Type t = member.Type; @@ -592,7 +592,7 @@ private static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSeri } else if (ObjectSerializationInfo.IsOptimizeTargetType(t)) { - if (!t.GetTypeInfo().IsValueType) + if (!t.IsValueType) { // As a nullable type (e.g. byte[] and string) we need to call WriteNil for null values. Label writeNonNilValueLabel = il.DefineLabel(); @@ -1060,7 +1060,7 @@ private static void BuildDeserializeInternalTryReadNil(Type type, ILGenerator il argReader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.TryReadNil); il.Emit(OpCodes.Brfalse_S, falseLabel); - if (type.GetTypeInfo().IsClass) + if (type.IsClass) { il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); @@ -1087,7 +1087,7 @@ private static void BuildDeserializeInternalDepthUnStep(ILGenerator il, ref Argu private static void BuildDeserializeInternalOnAfterDeserialize(Type type, ObjectSerializationInfo info, ILGenerator il, LocalBuilder localResult) { - if (type.GetTypeInfo().ImplementedInterfaces.All(x => x != typeof(IMessagePackSerializationCallbackReceiver))) + if (type.GetInterfaces().All(x => x != typeof(IMessagePackSerializationCallbackReceiver))) { return; } @@ -1197,7 +1197,7 @@ private static void BuildDeserializeInternalDeserializeValueAssignDirectly(TypeB } else if (ObjectSerializationInfo.IsOptimizeTargetType(t)) { - if (!t.GetTypeInfo().IsValueType) + if (!t.IsValueType) { // As a nullable type (e.g. byte[] and string) we need to first call TryReadNil // if (reader.TryReadNil()) @@ -1267,7 +1267,7 @@ private static void BuildDeserializeInternalDeserializeValueAssignLocalVariable( } else if (ObjectSerializationInfo.IsOptimizeTargetType(t)) { - if (!t.GetTypeInfo().IsValueType) + if (!t.IsValueType) { // As a nullable type (e.g. byte[] and string) we need to first call TryReadNil // if (reader.TryReadNil()) @@ -1383,8 +1383,8 @@ internal static class CodeGenHelpersTypeInfo internal static class EmitInfo { internal static readonly MethodInfo GetTypeFromHandle = ExpressionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))); - internal static readonly MethodInfo TypeGetProperty = ExpressionUtility.GetMethodInfo((Type t) => t.GetTypeInfo().GetProperty(default(string)!, default(BindingFlags))); - internal static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetTypeInfo().GetField(default(string)!, default(BindingFlags))); + internal static readonly MethodInfo TypeGetProperty = ExpressionUtility.GetMethodInfo((Type t) => t.GetProperty(default(string)!, default(BindingFlags))); + internal static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetField(default(string)!, default(BindingFlags))); internal static readonly MethodInfo GetCustomAttributeMessagePackFormatterAttribute = ExpressionUtility.GetMethodInfo(() => CustomAttributeExtensions.GetCustomAttribute(default(MemberInfo)!, default(bool))); internal static readonly MethodInfo ActivatorCreateInstance = ExpressionUtility.GetMethodInfo(() => Activator.CreateInstance(default(Type)!, default(object[]))); @@ -1451,13 +1451,12 @@ private ObjectSerializationInfo(Type type, EmittableMemberAndConstructorParamete internal static ObjectSerializationInfo? CreateOrNull(Type type, bool forceStringKey, bool contractless, bool allowPrivate) { - TypeInfo ti = type.GetTypeInfo(); - var isClass = ti.IsClass || ti.IsInterface || ti.IsAbstract; - var isClassRecord = isClass && IsClassRecord(ti); - var isStruct = ti.IsValueType; + var isClass = type.IsClass || type.IsInterface || type.IsAbstract; + var isClassRecord = isClass && IsClassRecord(type); + var isStruct = type.IsValueType; - MessagePackObjectAttribute? contractAttr = ti.GetCustomAttributes().FirstOrDefault(); - DataContractAttribute? dataContractAttr = ti.GetCustomAttribute(); + MessagePackObjectAttribute? contractAttr = type.GetCustomAttributes().FirstOrDefault(); + DataContractAttribute? dataContractAttr = type.GetCustomAttribute(); if (contractAttr == null && dataContractAttr == null && !forceStringKey && !contractless) { return null; @@ -1672,11 +1671,12 @@ bool AddEmittableMemberOrIgnore(bool isIntKeyMode, EmittableMember member, bool // GetConstructor IEnumerator? ctorEnumerator = null; - ConstructorInfo? ctor = ti.DeclaredConstructors.SingleOrDefault(x => x.GetCustomAttribute(false) is not null); + ConstructorInfo? ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) + .SingleOrDefault(x => x.GetCustomAttribute(false) is not null); if (ctor == null) { - ctorEnumerator = - ti.DeclaredConstructors.Where(x => !x.IsStatic && (allowPrivate || x.IsPublic)).OrderByDescending(x => x.GetParameters().Length) + ctorEnumerator = (allowPrivate ? type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) : + type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)).OrderByDescending(x => x.GetParameters().Length) .GetEnumerator(); if (ctorEnumerator.MoveNext()) @@ -1709,7 +1709,7 @@ bool AddEmittableMemberOrIgnore(bool isIntKeyMode, EmittableMember member, bool if (ctorParamIndexIntMembersDictionary.TryGetValue(ctorParamIndex, out paramMember)) { if ((item.ParameterType == paramMember.Type || - item.ParameterType.GetTypeInfo().IsAssignableFrom(paramMember.Type)) + item.ParameterType.IsAssignableFrom(paramMember.Type)) && paramMember.IsReadable) { constructorParameters.Add(new EmittableMemberAndConstructorParameter(paramMember, item)); @@ -1915,7 +1915,7 @@ private static IEnumerable GetAllProperties(Type type) } } - private static bool IsClassRecord(TypeInfo type) + private static bool IsClassRecord(Type type) { // The only truly unique thing about a C# 9 record class is the presence of a $ method, // which cannot be declared in C# because of the reserved characters in its name. diff --git a/src/MessagePack/Resolvers/DynamicUnionResolver.cs b/src/MessagePack/Resolvers/DynamicUnionResolver.cs index b65ca772d..01fe027e7 100644 --- a/src/MessagePack/Resolvers/DynamicUnionResolver.cs +++ b/src/MessagePack/Resolvers/DynamicUnionResolver.cs @@ -68,44 +68,42 @@ private static class FormatterCache static FormatterCache() { - TypeInfo ti = typeof(T).GetTypeInfo(); - if (ti.IsNullable()) + Type type = typeof(T); + if (type.IsNullable()) { - ti = ti.GenericTypeArguments[0].GetTypeInfo(); + type = type.GenericTypeArguments[0]; - var innerFormatter = DynamicUnionResolver.Instance.GetFormatterDynamic(ti.AsType()); + var innerFormatter = DynamicUnionResolver.Instance.GetFormatterDynamic(type); if (innerFormatter == null) { return; } - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), new object[] { innerFormatter }); return; } - TypeInfo? formatterTypeInfo = BuildType(typeof(T)); + TypeInfo? formatterTypeInfo = BuildType(type); if (formatterTypeInfo == null) { return; } - Formatter = (IMessagePackFormatter?)Activator.CreateInstance(formatterTypeInfo.AsType()); + Formatter = (IMessagePackFormatter?)Activator.CreateInstance(formatterTypeInfo); } } private static TypeInfo? BuildType(Type type) { - TypeInfo ti = type.GetTypeInfo(); - // order by key(important for use jump-table of switch) - UnionAttribute[] unionAttrs = ti.GetCustomAttributes().OrderBy(x => x.Key).ToArray(); + UnionAttribute[] unionAttrs = type.GetCustomAttributes().OrderBy(x => x.Key).ToArray(); if (unionAttrs.Length == 0) { return null; } - if (!ti.IsInterface && !ti.IsAbstract) + if (!type.IsInterface && !type.IsAbstract) { throw new MessagePackDynamicUnionResolverException("Union can only be interface or abstract class. Type:" + type.Name); } @@ -283,7 +281,7 @@ private static void BuildSerialize(Type type, UnionAttribute[] infos, MethodBuil il.EmitLdarg(1); il.EmitLdarg(2); - if (item.Attr.SubType.GetTypeInfo().IsValueType) + if (item.Attr.SubType.IsValueType) { il.Emit(OpCodes.Unbox_Any, item.Attr.SubType); } @@ -323,6 +321,14 @@ private static void BuildDeserialize(Type type, UnionAttribute[] infos, MethodBu il.MarkLabel(falseLabel); + var reader = new ArgumentField(il, 1); + + // options.Security.DepthStep(ref reader); + il.EmitLdarg(2); + il.EmitCall(getSecurityFromOptions); + reader.EmitLdarg(); + il.EmitCall(securityDepthStep); + // IFormatterResolver resolver = options.Resolver; LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); il.EmitLdarg(2); @@ -331,7 +337,6 @@ private static void BuildDeserialize(Type type, UnionAttribute[] infos, MethodBu // read-array header and validate, reader.ReadArrayHeader() != 2) throw; Label rightLabel = il.DefineLabel(); - var reader = new ArgumentField(il, 1); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); il.EmitLdc_I4(2); @@ -387,7 +392,7 @@ private static void BuildDeserialize(Type type, UnionAttribute[] infos, MethodBu il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(getDeserialize(item.Attr.SubType)); - if (item.Attr.SubType.GetTypeInfo().IsValueType) + if (item.Attr.SubType.IsValueType) { il.Emit(OpCodes.Box, item.Attr.SubType); } @@ -398,6 +403,14 @@ private static void BuildDeserialize(Type type, UnionAttribute[] infos, MethodBu il.MarkLabel(loopEnd); + // reader.Depth--; + reader.EmitLdarg(); + il.Emit(OpCodes.Dup); + il.EmitCall(readerDepthGet); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Sub_Ovf); + il.EmitCall(readerDepthSet); + il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); } @@ -422,6 +435,10 @@ private static bool IsZeroStartSequential(UnionAttribute[] infos) private static readonly Type refKvp = typeof(KeyValuePair).MakeByRefType(); private static readonly MethodInfo getFormatterWithVerify = typeof(FormatterResolverExtensions).GetRuntimeMethods().First(x => x.Name == "GetFormatterWithVerify"); private static readonly MethodInfo getResolverFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Resolver))!.GetMethod!; + private static readonly MethodInfo getSecurityFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Security))!.GetMethod!; + private static readonly MethodInfo securityDepthStep = typeof(MessagePackSecurity).GetRuntimeMethod(nameof(MessagePackSecurity.DepthStep), new[] { typeof(MessagePackReader).MakeByRefType() })!; + private static readonly MethodInfo readerDepthGet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth))!.GetMethod!; + private static readonly MethodInfo readerDepthSet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth))!.SetMethod!; private static readonly Func getSerialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Serialize", new[] { typeof(MessagePackWriter).MakeByRefType(), t, typeof(MessagePackSerializerOptions) })!; private static readonly Func getDeserialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Deserialize", new[] { typeof(MessagePackReader).MakeByRefType(), typeof(MessagePackSerializerOptions) })!; diff --git a/src/MessagePack/Resolvers/ExpandoObjectResolver.cs b/src/MessagePack/Resolvers/ExpandoObjectResolver.cs index ffc7b149c..11107efac 100644 --- a/src/MessagePack/Resolvers/ExpandoObjectResolver.cs +++ b/src/MessagePack/Resolvers/ExpandoObjectResolver.cs @@ -40,6 +40,7 @@ private class PrimitiveObjectWithExpandoMaps : PrimitiveObjectFormatter { protected override object DeserializeMap(ref MessagePackReader reader, int length, MessagePackSerializerOptions options) { + ExpandoObjectFormatter.ThrowIfMapTooLargeForUntrustedData(length, options); IMessagePackFormatter keyFormatter = options.Resolver.GetFormatterWithVerify(); IMessagePackFormatter? objectFormatter = options.Resolver.GetFormatterWithVerify(); IDictionary dictionary = new ExpandoObject(); diff --git a/src/MessagePack/Resolvers/ImmutableCollectionResolver.cs b/src/MessagePack/Resolvers/ImmutableCollectionResolver.cs index de645990e..b7eb81dc5 100644 --- a/src/MessagePack/Resolvers/ImmutableCollectionResolver.cs +++ b/src/MessagePack/Resolvers/ImmutableCollectionResolver.cs @@ -63,18 +63,15 @@ internal static class ImmutableCollectionGetFormatterHelper internal static object? GetFormatter(Type t) { - TypeInfo ti = t.GetTypeInfo(); - - if (ti.IsGenericType) + if (t.IsGenericType) { - Type genericType = ti.GetGenericTypeDefinition(); - TypeInfo genericTypeInfo = genericType.GetTypeInfo(); - var isNullable = genericTypeInfo.IsNullable(); - Type? nullableElementType = isNullable ? ti.GenericTypeArguments[0] : null; + Type genericType = t.GetGenericTypeDefinition(); + var isNullable = genericType.IsNullable(); + Type? nullableElementType = isNullable ? t.GenericTypeArguments[0] : null; if (FormatterMap.TryGetValue(genericType, out Type? formatterType)) { - return CreateInstance(formatterType, ti.GenericTypeArguments); + return CreateInstance(formatterType, t.GenericTypeArguments); } else if (isNullable && nullableElementType?.IsConstructedGenericType is true && nullableElementType.GetGenericTypeDefinition() == typeof(ImmutableArray<>)) { @@ -97,5 +94,10 @@ public static bool IsNullable(this System.Reflection.TypeInfo type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); } + + public static bool IsNullable(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); + } } } diff --git a/src/MessagePack/Resolvers/SkipClrVisibilityChecks.cs b/src/MessagePack/Resolvers/SkipClrVisibilityChecks.cs index c6fdfccfc..17d463fef 100644 --- a/src/MessagePack/Resolvers/SkipClrVisibilityChecks.cs +++ b/src/MessagePack/Resolvers/SkipClrVisibilityChecks.cs @@ -76,39 +76,39 @@ internal SkipClrVisibilityChecks(AssemblyBuilder assemblyBuilder, ModuleBuilder /// Scans a given type for references to non-public types and adds any assemblies that declare those types /// to a given set. /// - /// The type which may be internal. + /// The type which may be internal. /// The set of assemblies to add to where non-public types are found. - internal static void GetSkipVisibilityChecksRequirements(TypeInfo typeInfo, ImmutableHashSet.Builder referencedAssemblies) + internal static void GetSkipVisibilityChecksRequirements(Type type, ImmutableHashSet.Builder referencedAssemblies) { - if (typeInfo.IsArray) + if (type.IsArray) { - GetSkipVisibilityChecksRequirements(typeInfo.GetElementType()!.GetTypeInfo(), referencedAssemblies); + GetSkipVisibilityChecksRequirements(type.GetElementType()!, referencedAssemblies); } - AddTypeIfNonPublic(typeInfo); + AddTypeIfNonPublic(type); - foreach (Type arg in typeInfo.GenericTypeArguments) + foreach (Type arg in type.GenericTypeArguments) { AddTypeIfNonPublic(arg); } // We must walk each base type individually to ensure we don't miss any private members, // since even with BindingFlags.NonPublic, GetMembers will not return from base types. - for (TypeInfo? target = typeInfo; target is not null; target = target.BaseType?.GetTypeInfo()) + for (Type? target = type; target is not null; target = target.BaseType) { ScanDirectType(target); } - void ScanDirectType(TypeInfo typeInfo) + void ScanDirectType(Type type) { - foreach (MemberInfo member in typeInfo.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + foreach (MemberInfo member in type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) { switch (member) { case FieldInfo field: if (!field.IsPublic) { - referencedAssemblies.Add(typeInfo.Assembly.GetName()); + referencedAssemblies.Add(type.Assembly.GetName()); } AddTypeIfNonPublic(field.FieldType); @@ -116,7 +116,7 @@ void ScanDirectType(TypeInfo typeInfo) case PropertyInfo property: if (property.SetMethod?.IsPublic is false || property.GetMethod?.IsPublic is false) { - referencedAssemblies.Add(typeInfo.Assembly.GetName()); + referencedAssemblies.Add(type.Assembly.GetName()); } AddTypeIfNonPublic(property.PropertyType); @@ -124,7 +124,7 @@ void ScanDirectType(TypeInfo typeInfo) case ConstructorInfo constructorInfo: if (!constructorInfo.IsPublic) { - referencedAssemblies.Add(typeInfo.Assembly.GetName()); + referencedAssemblies.Add(type.Assembly.GetName()); } foreach (ParameterInfo parameter in constructorInfo.GetParameters()) diff --git a/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs b/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs index 96b54b315..ed2219b1e 100644 --- a/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs +++ b/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; @@ -195,6 +196,34 @@ public void MessagePackInputFormatterSupportsXMsgPack() inputFormatter.SupportedMediaTypes.Is(MsgPackContentType); } + [Fact] + public async Task MessagePackInputFormatterDefaultsToUntrustedData() + { + const long value1 = 0x100000001; + const long value2 = 0x200000002; + + var messagePackBinary = MessagePackSerializer.Serialize(new Dictionary + { + [value1] = 1, + [value2] = 2, + }); + + var httpContext = new DefaultHttpContext(); + httpContext.Features.Set(new TestResponseFeature()); + httpContext.Request.Body = new NonSeekableReadStream(messagePackBinary); + httpContext.Request.ContentType = MsgPackContentType; + + InputFormatterContext inputFormatterContext = this.CreateInputFormatterContext(typeof(Dictionary), httpContext); + var inputFormatter = new MessagePackInputFormatter(); + + InputFormatterResult result = await inputFormatter.ReadAsync(inputFormatterContext); + + Assert.False(result.HasError); + var dictionary = Assert.IsType>(result.Model); + Assert.Equal(EqualityComparer.Default.GetHashCode(value1), EqualityComparer.Default.GetHashCode(value2)); + Assert.NotEqual(dictionary.Comparer.GetHashCode(value1), dictionary.Comparer.GetHashCode(value2)); + } + /// /// JsonOutputFormatterTests.cs#L453. /// diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(Namespace)/Formatters.MessagePack.GeneratedMessagePackResolver.MyTestNamespace.IMyTypeFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(Namespace)/Formatters.MessagePack.GeneratedMessagePackResolver.MyTestNamespace.IMyTypeFormatter.g.cs index be91b6294..f6e75173c 100644 --- a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(Namespace)/Formatters.MessagePack.GeneratedMessagePackResolver.MyTestNamespace.IMyTypeFormatter.g.cs +++ b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(Namespace)/Formatters.MessagePack.GeneratedMessagePackResolver.MyTestNamespace.IMyTypeFormatter.g.cs @@ -32,7 +32,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::MyTestNames if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair)) { writer.WriteArrayHeader(2); - writer.WriteInt32(keyValuePair.Key); + writer.Write(keyValuePair.Key); switch (keyValuePair.Value) { case 0: diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(NestingClass)/Formatters.MessagePack.GeneratedMessagePackResolver.ContainingClass.IMyTypeFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(NestingClass)/Formatters.MessagePack.GeneratedMessagePackResolver.ContainingClass.IMyTypeFormatter.g.cs index 63f0c831e..5fd854188 100644 --- a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(NestingClass)/Formatters.MessagePack.GeneratedMessagePackResolver.ContainingClass.IMyTypeFormatter.g.cs +++ b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(NestingClass)/Formatters.MessagePack.GeneratedMessagePackResolver.ContainingClass.IMyTypeFormatter.g.cs @@ -32,7 +32,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::ContainingC if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair)) { writer.WriteArrayHeader(2); - writer.WriteInt32(keyValuePair.Key); + writer.Write(keyValuePair.Key); switch (keyValuePair.Value) { case 0: diff --git a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(None)/Formatters.MessagePack.GeneratedMessagePackResolver.IMyTypeFormatter.g.cs b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(None)/Formatters.MessagePack.GeneratedMessagePackResolver.IMyTypeFormatter.g.cs index 7d39b6866..b849b5ca4 100644 --- a/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(None)/Formatters.MessagePack.GeneratedMessagePackResolver.IMyTypeFormatter.g.cs +++ b/tests/MessagePack.SourceGenerator.Tests/Resources/UnionFormatter(None)/Formatters.MessagePack.GeneratedMessagePackResolver.IMyTypeFormatter.g.cs @@ -31,7 +31,7 @@ public void Serialize(ref MsgPack::MessagePackWriter writer, global::IMyType val if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair)) { writer.WriteArrayHeader(2); - writer.WriteInt32(keyValuePair.Key); + writer.Write(keyValuePair.Key); switch (keyValuePair.Value) { case 0: diff --git a/tests/MessagePack.Tests/ExpandoObjectTests.cs b/tests/MessagePack.Tests/ExpandoObjectTests.cs index 61ae7b0bf..40546e58e 100644 --- a/tests/MessagePack.Tests/ExpandoObjectTests.cs +++ b/tests/MessagePack.Tests/ExpandoObjectTests.cs @@ -3,6 +3,7 @@ #if !UNITY_2018_3_OR_NEWER +using System.Collections.Generic; using System.Dynamic; using System.Runtime.Serialization; using MessagePack.Resolvers; @@ -76,6 +77,24 @@ public void ExpandoObject_DeepGraphContainsCustomTypes() Assert.Equal(expando.Other.OtherProperty, expando2.Other.OtherProperty); } + [Fact] + [Trait("CWE", "407")] + public void ExpandoObject_UntrustedDataRejectsLargeMaps() + { + byte[] msgpack = CreateMapWithNilValues(1025); + + Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, ExpandoObjectResolver.Options)); + } + + [Fact] + [Trait("CWE", "407")] + public void ExpandoObjectNestedMap_UntrustedDataRejectsLargeMaps() + { + byte[] msgpack = CreateMapWithNilValues(1025); + + Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, ExpandoObjectResolver.Options)); + } + #if !UNITY_2018_3_OR_NEWER [Fact] @@ -106,6 +125,17 @@ public class CustomObject [DataMember] public string OtherProperty { get; set; } } + + private static byte[] CreateMapWithNilValues(int count) + { + var dictionary = new Dictionary(); + for (int index = 0; index < count; index++) + { + dictionary.Add("k" + index.ToString(System.Globalization.CultureInfo.InvariantCulture), null); + } + + return MessagePackSerializer.Serialize(dictionary, MessagePackSerializerOptions.Standard); + } } } diff --git a/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs b/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs index 7e35b2448..899f839a7 100644 --- a/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs +++ b/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs @@ -94,6 +94,18 @@ public void EnsureSpecCompatibilityTest(BlitContainer data) EnsureSpecCompatibility(data.Array); } + [Fact] + [Trait("CWE", "789")] + public void BlitRejectsByteLengthThatExceedsExtensionBody() + { + MessagePackSerializerOptions options = MessagePackSerializerOptions.Standard.WithResolver(new WithUnityBlitResolver()); + byte[] payload = { 0xC7, 0x06, unchecked((byte)ReservedExtensionTypeCodes.UnityInt), 0xCE, 0x00, 0x00, 0x00, 0x08, 0xC3 }; + + var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); + var inner = Assert.IsType(ex.InnerException); + Assert.Contains("Invalid Unity blit extension length", inner.Message); + } + public class WithUnityBlitResolver : IFormatterResolver { public IMessagePackFormatter GetFormatter() diff --git a/tests/MessagePack.Tests/FormatterTest.cs b/tests/MessagePack.Tests/FormatterTest.cs index d74e4ba5e..fdb496b06 100644 --- a/tests/MessagePack.Tests/FormatterTest.cs +++ b/tests/MessagePack.Tests/FormatterTest.cs @@ -227,8 +227,12 @@ public void DateTimeOffsetTest() { string id = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Tokyo Standard Time" : "Cuba"; DateTimeOffset now = new DateTime(DateTime.UtcNow.Ticks + TimeZoneInfo.FindSystemTimeZoneById(id).BaseUtcOffset.Ticks, DateTimeKind.Local); - var binary = MessagePackSerializer.Serialize(now); - MessagePackSerializer.Deserialize(binary).Is(now); + AssertRoundtrip(now); + + AssertRoundtrip(DateTimeOffset.Now); + + // Try specific offset values because CI/PR builds run on agents that run on the UTC time zone. + AssertRoundtrip(DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(4))); } [Fact] @@ -323,5 +327,21 @@ public void HalfTest() } #endif + + private static DateTimeOffset AssertRoundtrip(DateTimeOffset value) + { + var result = MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(value)); + result.Is(value, DateTimeOffsetEqualityComparer.Instance); + return result; + } + + private class DateTimeOffsetEqualityComparer : IEqualityComparer + { + internal static readonly DateTimeOffsetEqualityComparer Instance = new(); + + public bool Equals(DateTimeOffset x, DateTimeOffset y) => x.EqualsExact(y); + + public int GetHashCode(DateTimeOffset obj) => obj.UtcDateTime.GetHashCode(); + } } } diff --git a/tests/MessagePack.Tests/LZ4Test.cs b/tests/MessagePack.Tests/LZ4Test.cs index f314b572a..c78ae2964 100644 --- a/tests/MessagePack.Tests/LZ4Test.cs +++ b/tests/MessagePack.Tests/LZ4Test.cs @@ -1,6 +1,7 @@ // Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Buffers; using System.Linq; using Xunit; using Xunit.Abstractions; @@ -22,6 +23,64 @@ public LZ4Test(ITestOutputHelper logger) #endif + [Theory] + [InlineData(MessagePackCompression.Lz4Block)] + [InlineData(MessagePackCompression.Lz4BlockArray)] + [Trait("CWE", "409")] + public void Lz4RejectsDeclaredOutputOverMaximumBeforeAllocating(MessagePackCompression compression) + { + byte[] payload = compression == MessagePackCompression.Lz4Block + ? [0xC7, 0x06, 0x63, 0xD2, 0x7F, 0xFF, 0xFF, 0xFF, 0x00] + : [0x92, 0xC7, 0x05, 0x62, 0xD2, 0x7F, 0xFF, 0xFF, 0xFF, 0xC4, 0x01, 0x00]; + var arrayPool = new ThrowingArrayPool(1024); + var options = MessagePackSerializerOptions.Standard + .WithCompression(compression) + .WithSecurity(MessagePackSecurity.UntrustedData) + .WithPool(new SequencePool(1, arrayPool)); + + MessagePackSerializationException ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); + + Assert.Contains("exceeds the configured maximum", FlattenMessages(ex)); + Assert.Null(arrayPool.LargestRequestedLength); + } + + [Fact] + [Trait("CWE", "409")] + public void Lz4BlockArrayRejectsTotalDeclaredOutputOverMaximum() + { + byte[] payload = + [ + 0x93, + 0xC7, 0x02, 0x62, 0x04, 0x04, + 0xC4, 0x05, 0x40, 0x20, 0x20, 0x20, 0x20, + 0xC4, 0x05, 0x40, 0x20, 0x20, 0x20, 0x20, + ]; + var options = MessagePackSerializerOptions.Standard + .WithCompression(MessagePackCompression.Lz4BlockArray) + .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(7)); + + MessagePackSerializationException ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); + + Assert.Contains("exceeds the configured maximum", FlattenMessages(ex)); + } + + [Theory] + [InlineData(MessagePackCompression.Lz4Block)] + [InlineData(MessagePackCompression.Lz4BlockArray)] + [Trait("CWE", "409")] + public void Lz4AllowsHighlyCompressiblePayloadWithinMaximum(MessagePackCompression compression) + { + string data = new string(' ', 100_000); + var options = MessagePackSerializerOptions.Standard + .WithCompression(compression) + .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(200_000)); + + byte[] payload = MessagePackSerializer.Serialize(data, options); + string actual = MessagePackSerializer.Deserialize(payload, options); + + Assert.Equal(data, actual); + } + [Fact] public void Lz4Compress() { @@ -32,6 +91,47 @@ public void Lz4Compress() Execute(10000); } + [Fact] + [Trait("CWE", "125")] + public void Lz4BlockRejectsTruncatedLiteralRun() + { + const int extensionByteCount = 1024; + int uncompressedLength = 15 + (255 * extensionByteCount) + 16; + + byte[] sizeHeader = + { + 0xCE, + (byte)(uncompressedLength >> 24), + (byte)(uncompressedLength >> 16), + (byte)(uncompressedLength >> 8), + (byte)uncompressedLength, + }; + + byte[] lz4 = new byte[1 + extensionByteCount]; + lz4[0] = 0xF0; + for (int i = 1; i < lz4.Length; i++) + { + lz4[i] = 0xFF; + } + + int bodyLength = sizeHeader.Length + lz4.Length; + byte[] payload = new byte[6 + bodyLength]; + int offset = 0; + payload[offset++] = 0xC9; + payload[offset++] = (byte)(bodyLength >> 24); + payload[offset++] = (byte)(bodyLength >> 16); + payload[offset++] = (byte)(bodyLength >> 8); + payload[offset++] = (byte)bodyLength; + payload[offset++] = 99; + System.Array.Copy(sizeHeader, 0, payload, offset, sizeHeader.Length); + offset += sizeHeader.Length; + System.Array.Copy(lz4, 0, payload, offset, lz4.Length); + + var options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block); + + Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); + } + private void Execute(int count) { // Large @@ -82,5 +182,37 @@ private static void SequenceStructuralEqual(SharedData.SimpleStringKeyData[] act actual[i].Prop3.Is(expected[i].Prop3); } } + + private static string FlattenMessages(System.Exception ex) + { + return ex.InnerException is null ? ex.Message : ex.Message + " " + FlattenMessages(ex.InnerException); + } + + private class ThrowingArrayPool : ArrayPool + { + private readonly int maximumLength; + + internal ThrowingArrayPool(int maximumLength) + { + this.maximumLength = maximumLength; + } + + internal int? LargestRequestedLength { get; private set; } + + public override byte[] Rent(int minimumLength) + { + this.LargestRequestedLength = this.LargestRequestedLength.HasValue ? System.Math.Max(this.LargestRequestedLength.Value, minimumLength) : minimumLength; + if (minimumLength > this.maximumLength) + { + throw new System.InvalidOperationException("Unexpected decompression allocation request: " + minimumLength); + } + + return new byte[minimumLength]; + } + + public override void Return(byte[] array, bool clearArray = false) + { + } + } } } diff --git a/tests/MessagePack.Tests/MessagePackReaderTests.cs b/tests/MessagePack.Tests/MessagePackReaderTests.cs index 7ac748023..3278386c2 100644 --- a/tests/MessagePack.Tests/MessagePackReaderTests.cs +++ b/tests/MessagePack.Tests/MessagePackReaderTests.cs @@ -101,6 +101,32 @@ public void ReadMapHeader_MitigatesLargeAllocations() }); } + [Fact] + [Trait("CWE", "190")] + public void ReadMapHeader_MitigatesLargeAllocations_WhenMinimumPayloadLengthOverflowsInt32() + { + byte[] msgpack = { MessagePackCode.Map32, 0x40, 0, 0, 0 }; + + Assert.Throws(() => + { + var reader = new MessagePackReader(msgpack); + reader.ReadMapHeader(); + }); + } + + [Fact] + [Trait("CWE", "190")] + public void SkipMap_MitigatesLargeAllocations_WhenMinimumPayloadLengthOverflowsInt32() + { + byte[] msgpack = { MessagePackCode.Map32, 0x40, 0, 0, 0 }; + + Assert.Throws(() => + { + var reader = new MessagePackReader(msgpack); + reader.Skip(); + }); + } + [Fact] public void TryReadMapHeader() { @@ -330,6 +356,49 @@ public void ReadRaw() Assert.True(reader.End); } + [Fact] + [Trait("CWE", "674")] + public void Skip_DeeplyNestedArrays_DoesNotOverflowStack() + { + const int depth = 100_000; + byte[] msgpack = new byte[depth + 1]; + + for (int i = 0; i < depth; i++) + { + msgpack[i] = MessagePackCode.MinFixArray + 1; + } + + msgpack[^1] = MessagePackCode.Nil; + + MessagePackReader reader = new(msgpack); + + reader.Skip(); + + Assert.True(reader.End); + } + + [Fact] + [Trait("CWE", "674")] + public void Skip_DeeplyNestedMaps_DoesNotOverflowStack() + { + const int depth = 100_000; + byte[] msgpack = new byte[(depth * 2) + 1]; + + for (int i = 0; i < depth; i++) + { + msgpack[i * 2] = MessagePackCode.MinFixMap + 1; + msgpack[(i * 2) + 1] = MessagePackCode.Nil; + } + + msgpack[^1] = MessagePackCode.Nil; + + MessagePackReader reader = new(msgpack); + + reader.Skip(); + + Assert.True(reader.End); + } + [Fact] public void Depth() { @@ -400,6 +469,15 @@ void AssertIncomplete(WriterEncoder encoder, ReadOperation decoder, bool v AssertIncomplete((ref MessagePackWriter writer) => writer.Write(0xff), (ref MessagePackReader reader) => reader.ReadUInt64()); } + [Fact] + [Trait("CWE", "789")] + public void ReadDateTime_RejectsInvalidExtensionLengthBeforeBuffering() + { + byte[] payload = [MessagePackCode.Ext32, 0x00, 0x10, 0x00, 0x00, 0xff]; + + Assert.Throws(() => new MessagePackReader(new ReadOnlySequence(payload)).ReadDateTime()); + } + [Fact] public void CreatePeekReader() { diff --git a/tests/MessagePack.Tests/MessagePackSecurityTests.cs b/tests/MessagePack.Tests/MessagePackSecurityTests.cs index 499425777..b517810b8 100644 --- a/tests/MessagePack.Tests/MessagePackSecurityTests.cs +++ b/tests/MessagePack.Tests/MessagePackSecurityTests.cs @@ -22,12 +22,14 @@ public MessagePackSecurityTests(ITestOutputHelper logger) public void Untrusted() { Assert.True(MessagePackSecurity.UntrustedData.HashCollisionResistant); + Assert.Equal(64 * 1024 * 1024, MessagePackSecurity.UntrustedData.MaximumDecompressedSize); } [Fact] public void Trusted() { Assert.False(MessagePackSecurity.TrustedData.HashCollisionResistant); + Assert.Equal(int.MaxValue, MessagePackSecurity.TrustedData.MaximumDecompressedSize); } [Fact] @@ -37,6 +39,15 @@ public void WithHashCollisionResistant() Assert.True(MessagePackSecurity.TrustedData.WithHashCollisionResistant(true).HashCollisionResistant); } + [Fact] + [Trait("CWE", "409")] + public void WithMaximumDecompressedSize() + { + Assert.Same(MessagePackSecurity.UntrustedData, MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(64 * 1024 * 1024)); + Assert.Throws(() => MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(-1)); + Assert.Equal(1024, MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(1024).MaximumDecompressedSize); + } + [Fact] public void EqualityComparer_CollisionResistance_Int64() { diff --git a/tests/MessagePack.Tests/MessagePackSerializerTest.cs b/tests/MessagePack.Tests/MessagePackSerializerTest.cs index 211a35143..fc4915909 100644 --- a/tests/MessagePack.Tests/MessagePackSerializerTest.cs +++ b/tests/MessagePack.Tests/MessagePackSerializerTest.cs @@ -209,6 +209,18 @@ public async Task SerializeAndDeserializeAsync_MultipleValues_SeekableStream(boo Assert.Equal(3, await MessagePackSerializer.DeserializeAsync(stream)); } + [Fact] + [Trait("CWE", "674")] + public void StackDepthCheck_ConvertToJsonTypelessExtension() + { + const int maxDepth = 3; + byte[] msgpack = BuildNestedTypelessExtension(maxDepth + 1); + var options = MessagePackSerializerOptions.Standard + .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(maxDepth)); + + AssertConvertToJsonRecursionCheckThrows(new ReadOnlySequence(msgpack), options); + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -275,6 +287,18 @@ public void StackDepthCheck_DynamicObjectResolver() Assert.IsType(ex.InnerException); } + [Fact] + [Trait("CWE", "674")] + public void StackDepthCheck_DynamicUnionResolver() + { + byte[] msgpack = MessagePackSerializer.Serialize(new DepthCheckedUnionBranch()); + var options = MessagePackSerializerOptions.Standard + .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(1)); + + var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); + Assert.IsType(ex.InnerException); + } + #endif private delegate void WriterHelper(ref MessagePackWriter writer); @@ -318,6 +342,28 @@ private static void AssertConvertToJsonRecursionCheckThrows(ReadOnlySequence(ex.InnerException); } + private static byte[] BuildNestedTypelessExtension(int levels) + { + byte[] msgpack = new byte[(levels * 6) + 2]; + int offset = msgpack.Length; + msgpack[--offset] = (byte)'x'; + msgpack[--offset] = 0xa1; + int innerLength = 2; + + for (int level = 0; level < levels; level++) + { + msgpack[--offset] = unchecked((byte)ReservedExtensionTypeCodes.TypelessFormatter); + msgpack[--offset] = (byte)innerLength; + msgpack[--offset] = (byte)(innerLength >> 8); + msgpack[--offset] = (byte)(innerLength >> 16); + msgpack[--offset] = (byte)(innerLength >> 24); + msgpack[--offset] = 0xc9; + innerLength += 6; + } + + return msgpack; + } + [DataContract] public class RecursiveObjectGraph { @@ -414,4 +460,30 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } } + + [MessagePackObject(keyAsPropertyName: true)] + public class SkipUnknownMemberTarget + { + public int Known { get; set; } + } + + [Union(0, typeof(DepthCheckedUnionLeaf))] + [Union(999, typeof(DepthCheckedUnionBranch))] + public interface IDepthCheckedUnionNode + { + } + + [MessagePackObject] + public class DepthCheckedUnionLeaf : IDepthCheckedUnionNode + { + [Key(0)] + public int Value { get; set; } + } + + [MessagePackObject] + public class DepthCheckedUnionBranch : IDepthCheckedUnionNode + { + [Key(0)] + public IDepthCheckedUnionNode Child { get; set; } + } } diff --git a/tests/MessagePack.Tests/MessagePackSerializerTypelessTests.cs b/tests/MessagePack.Tests/MessagePackSerializerTypelessTests.cs index c92c8c994..da7246a43 100644 --- a/tests/MessagePack.Tests/MessagePackSerializerTypelessTests.cs +++ b/tests/MessagePack.Tests/MessagePackSerializerTypelessTests.cs @@ -4,12 +4,10 @@ #if !UNITY_2018_3_OR_NEWER using System; +using System.Collections.Generic; using System.Runtime.Serialization; -using MessagePack; using MessagePack.Formatters; using MessagePack.Resolvers; -using Xunit; -using Xunit.Abstractions; public class MessagePackSerializerTypelessTests { @@ -47,6 +45,24 @@ public void SerializationOfDisallowedType() Assert.IsType(ex.InnerException); } + [Theory] + [MemberData(nameof(DisallowedNestedTypeData))] + [Trait("CWE", "502")] + public void SerializationOfDisallowedNestedType(object value) + { + var myOptions = new MyTypelessOptions(); + byte[] msgpack = MessagePackSerializer.Typeless.Serialize(value, myOptions); + this.logger.WriteLine(MessagePackSerializer.ConvertToJson(msgpack, myOptions)); + var ex = Assert.Throws(() => MessagePackSerializer.Typeless.Deserialize(msgpack, myOptions)); + Assert.IsType(ex.InnerException); + } + + public static IEnumerable DisallowedNestedTypeData() + { + yield return new object[] { new MyObject[] { new() { SomeValue = 5 } } }; + yield return new object[] { new List { new() { SomeValue = 5 } } }; + } + [Fact(Skip = "Known bug https://github.com/neuecc/MessagePack-CSharp/issues/651")] public void DecimalShouldBeDeserializedAsDecimal() { @@ -137,7 +153,7 @@ internal MyTypelessOptions(MyTypelessOptions copyFrom) { } - public override void ThrowIfDeserializingTypeIsDisallowed(Type type) + protected override void ThrowIfDeserializingTypeIsDisallowedCore(Type type) { if (type == typeof(MyObject)) { diff --git a/tests/MessagePack.Tests/MultiDimensionalArrayTest.cs b/tests/MessagePack.Tests/MultiDimensionalArrayTest.cs index f4a0a2619..591e5472f 100644 --- a/tests/MessagePack.Tests/MultiDimensionalArrayTest.cs +++ b/tests/MessagePack.Tests/MultiDimensionalArrayTest.cs @@ -10,7 +10,7 @@ namespace MessagePack.Tests { - public class MultiDimensionalArrayTest + public class MultiDimensionalArrayTest(ITestOutputHelper logger) { private T Convert(T value) { @@ -67,5 +67,61 @@ public void MultiDimensional(int dataI, int dataJ, int dataK, int dataL) } } } + + [Fact] + [Trait("CWE", "789")] + public void RejectsTwoDimensionalArrayWithMismatchedElementCount() + { + byte[] payload = + { + 0x93, + 0xCE, 0x00, 0x00, 0x07, 0xD0, + 0xCE, 0x00, 0x00, 0x07, 0xD0, + 0x90, + }; + + AssertRejects(payload); + } + + [Fact] + [Trait("CWE", "789")] + public void RejectsThreeDimensionalArrayWithMismatchedElementCount() + { + byte[] payload = + { + 0x94, + 0xCC, 0x80, + 0xCC, 0x80, + 0xCC, 0x80, + 0x90, + }; + + AssertRejects(payload); + } + + [Fact] + [Trait("CWE", "789")] + public void RejectsFourDimensionalArrayWithMismatchedElementCount() + { + byte[] payload = + { + 0x95, + 0x20, + 0x20, + 0x20, + 0x20, + 0x90, + }; + + AssertRejects(payload); + } + + private void AssertRejects(byte[] payload) + { + var options = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData); + + var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); + logger.WriteLine(ex.ToString()); + } } } diff --git a/tests/MessagePack.Tests/StandardClassLibraryFormatterTests.cs b/tests/MessagePack.Tests/StandardClassLibraryFormatterTests.cs index 2a86f2d49..991956eba 100644 --- a/tests/MessagePack.Tests/StandardClassLibraryFormatterTests.cs +++ b/tests/MessagePack.Tests/StandardClassLibraryFormatterTests.cs @@ -36,6 +36,27 @@ public void SystemType_Serializable_Null() Assert.Equal(type, type2); } + [Fact] + public void SystemType_DeserializeUsesLoadType() + { + byte[] msgpack = MessagePackSerializer.Serialize(new TypeHolder { Type = typeof(Uri) }, MessagePackSerializerOptions.Standard); + var options = new RejectingTypeLoadOptions(typeof(Uri)); + + var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); + Assert.IsType(ex.InnerException); + Assert.Equal(1, options.LoadTypeCalls); + } + + [Fact] + public void SystemType_DeserializeRejectsDisallowedType() + { + byte[] msgpack = MessagePackSerializer.Serialize(new TypeHolder { Type = typeof(Uri) }, MessagePackSerializerOptions.Standard); + var options = new DisallowingTypeOptions(typeof(Uri)); + + var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); + Assert.IsType(ex.InnerException); + } + [Fact] public void DeserializeByteArrayFromFixArray() { @@ -145,5 +166,68 @@ private T Roundtrip(T value, bool breakupBuffer = false) return MessagePackSerializer.Deserialize(msgpack, MessagePackSerializerOptions.Standard); } } + + [MessagePackObject] + public class TypeHolder + { + [Key(0)] + public Type Type { get; set; } + } + + private class RejectingTypeLoadOptions : MessagePackSerializerOptions + { + private readonly Type rejectedType; + + internal RejectingTypeLoadOptions(Type rejectedType) + : base(MessagePackSerializerOptions.Standard) + { + this.rejectedType = rejectedType; + } + + private RejectingTypeLoadOptions(RejectingTypeLoadOptions copyFrom) + : base(copyFrom) + { + this.rejectedType = copyFrom.rejectedType; + this.LoadTypeCalls = copyFrom.LoadTypeCalls; + } + + public int LoadTypeCalls { get; private set; } + + public override Type LoadType(string typeName) + { + Type type = base.LoadType(typeName); + this.LoadTypeCalls++; + return type == this.rejectedType ? null : type; + } + + protected override MessagePackSerializerOptions Clone() => new RejectingTypeLoadOptions(this); + } + + private class DisallowingTypeOptions : MessagePackSerializerOptions + { + private readonly Type rejectedType; + + internal DisallowingTypeOptions(Type rejectedType) + : base(MessagePackSerializerOptions.Standard) + { + this.rejectedType = rejectedType; + } + + private DisallowingTypeOptions(DisallowingTypeOptions copyFrom) + : base(copyFrom) + { + this.rejectedType = copyFrom.rejectedType; + } + + public override void ThrowIfDeserializingTypeIsDisallowed(Type type) + { + if (type == this.rejectedType) + { + throw new TypeAccessException(); + } + } + + protected override MessagePackSerializerOptions Clone() => new DisallowingTypeOptions(this); + } } } diff --git a/tests/MessagePack.Tests/ToJsonTest.cs b/tests/MessagePack.Tests/ToJsonTest.cs index 8069ea266..99fceee42 100644 --- a/tests/MessagePack.Tests/ToJsonTest.cs +++ b/tests/MessagePack.Tests/ToJsonTest.cs @@ -47,6 +47,29 @@ public void ComplexToJson() this.JsonConvert(json, LZ4Standard).Is(json); } + [Theory] + [InlineData(false)] + [InlineData(true)] + [Trait("CWE", "674")] + public void ConvertFromJsonRejectsExcessiveNesting(bool compression) + { + var options = MessagePackSerializerOptions.Standard + .WithCompression(compression ? MessagePackCompression.Lz4Block : MessagePackCompression.None) + .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(3)); + + Assert.Throws(() => MessagePackSerializer.ConvertFromJson("[[[[1]]]]", options)); + } + + [Fact] + [Trait("CWE", "674")] + public void ConvertFromJsonSkipsLongSeparatorRunIteratively() + { + var json = new string(',', 200_000) + "null"; + var msgpack = MessagePackSerializer.ConvertFromJson(json); + + MessagePackSerializer.ConvertToJson(msgpack).Is("null"); + } + [Fact] public void FloatJson() { diff --git a/tests/MessagePack.Tests/UnsafeMemoryTest.cs b/tests/MessagePack.Tests/UnsafeMemoryTest.cs index c9b5f023e..8c3af1431 100644 --- a/tests/MessagePack.Tests/UnsafeMemoryTest.cs +++ b/tests/MessagePack.Tests/UnsafeMemoryTest.cs @@ -3,15 +3,9 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using MessagePack.Formatters; using MessagePack.Internal; using Nerdbank.Streams; -using Xunit; namespace MessagePack.Tests { @@ -70,6 +64,35 @@ public void WriteRaw() } } + [Fact] + public void WriteRaw_LargerSpan() + { + ReadOnlySpan src = new byte[MessagePackRange.MaxFixStringLength + 15]; + Sequence dst = new(); + + // x86 + for (int i = 1; i <= MessagePackRange.MaxFixStringLength; i++) + { + dst.Reset(); + MessagePackWriter dstWriter = new(dst); + (typeof(UnsafeMemory32).GetMethod("WriteRaw" + i).CreateDelegate(typeof(WriteDelegate)) as WriteDelegate).Invoke(ref dstWriter, src); + dstWriter.Flush(); + dst.Length.Is(i); + src[0..i].SequenceEqual(CodeGenHelpers.GetSpanFromSequence(dst.AsReadOnlySequence)).IsTrue(); + } + + // x64 + for (int i = 1; i <= MessagePackRange.MaxFixStringLength; i++) + { + dst.Reset(); + var dstWriter = new MessagePackWriter(dst); + (typeof(UnsafeMemory64).GetMethod("WriteRaw" + i).CreateDelegate(typeof(WriteDelegate)) as WriteDelegate).Invoke(ref dstWriter, src); + dstWriter.Flush(); + dst.Length.Is(i); + src[0..i].SequenceEqual(CodeGenHelpers.GetSpanFromSequence(dst.AsReadOnlySequence)).IsTrue(); + } + } + #endif } }