diff --git a/.gitignore b/.gitignore
index 7e74065d1..7209c6cbc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,9 @@ bld/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
+# Jetbrains Rider cache directory
+.idea/
+
# Visual Studio 2017 auto generated files
Generated\ Files/
@@ -65,7 +68,6 @@ StyleCopReport.xml
*_p.c
*_h.h
*.ilk
-*.meta
*.obj
*.iobj
*.pch
@@ -353,12 +355,21 @@ MigrationBackup/
# mac-created file to track user view preferences for a directory
.DS_Store
+# Analysis results
+*.sarif
+
# Unity
src/MessagePack.UnityClient/bin/*
src/MessagePack.UnityClient/Library/*
src/MessagePack.UnityClient/obj/*
src/MessagePack.UnityClient/Temp/*
+src/MessagePack.UnityClient/UserSettings/*
+src/MessagePack.UnityClient/Assets/Packages/
# BenchmarkDotNet results
BenchmarkDotNet.Artifacts/
+
+src/MessagePack.UnityClient/.vsconfig
+
+*.lscache
diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml
index 4021879d8..c9f5fc170 100644
--- a/azure-pipelines/build.yml
+++ b/azure-pipelines/build.yml
@@ -26,7 +26,7 @@ jobs:
- job: Linux
pool:
- vmImage: Ubuntu 20.04
+ vmImage: ubuntu-22.04
steps:
- checkout: self
fetchDepth: 0 # avoid shallow clone so nbgv can do its work.
diff --git a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
index 8d1a7c7e2..676074044 100644
--- a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
+++ b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
@@ -46,6 +46,9 @@
Code\DynamicAssembly.cs
+
+ Code\DynamicAssemblyFactory.cs
+
Code\ExpressionUtility.cs
diff --git a/sandbox/Sandbox/Generated.cs b/sandbox/Sandbox/Generated.cs
index 696478189..16b349886 100644
--- a/sandbox/Sandbox/Generated.cs
+++ b/sandbox/Sandbox/Generated.cs
@@ -3093,6 +3093,53 @@ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::
}
}
+ public sealed class SimpleGenericDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter>
+ {
+
+ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::SharedData.SimpleGenericData value, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ if (value == null)
+ {
+ writer.WriteNil();
+ return;
+ }
+
+ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver;
+ writer.WriteArrayHeader(1);
+ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Value, options);
+ }
+
+ public global::SharedData.SimpleGenericData Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ if (reader.TryReadNil())
+ {
+ return null;
+ }
+
+ options.Security.DepthStep(ref reader);
+ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver;
+ var length = reader.ReadArrayHeader();
+ var __Value__ = default(T);
+
+ for (int i = 0; i < length; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ __Value__ = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options);
+ break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+
+ var ____result = new global::SharedData.SimpleGenericData(__Value__);
+ reader.Depth--;
+ return ____result;
+ }
+ }
+
public sealed class SimpleIntKeyDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter
{
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.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs
index 29d8ca7d7..02da01f9e 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs
@@ -765,7 +765,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.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs
index 2d9328880..98932401f 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs
index 44812d1a0..128e52e42 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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;
#pragma warning disable SA1402 // File may only contain a single type
#pragma warning disable SA1649 // File name should match first type name
@@ -62,6 +60,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];
@@ -148,6 +147,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];
@@ -244,6 +244,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;
@@ -291,4 +293,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.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs
index da2444b41..1cc840e5b 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs
@@ -649,9 +649,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.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs
index 7e410e446..90ee1698f 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs
@@ -20,6 +20,11 @@ internal class DynamicAssembly
// don't expose ModuleBuilder
//// public ModuleBuilder ModuleBuilder { get { return moduleBuilder; } }
+ ///
+ /// Initializes a new instance of the class.
+ /// Please use instead in order to work across different AssemblyLoadContext that may have duplicate modules.
+ ///
+ /// Name of the module to be generated.
public DynamicAssembly(string moduleName)
{
#if NETFRAMEWORK // We don't ship a net472 target, but we might add one for debugging purposes
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssemblyFactory.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssemblyFactory.cs
new file mode 100644
index 000000000..1529e07bf
--- /dev/null
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/DynamicAssemblyFactory.cs
@@ -0,0 +1,63 @@
+// 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;
+using System.Collections.Generic;
+using System.Reflection;
+
+#if NET
+using System.Runtime.Loader;
+#endif
+
+namespace MessagePack.Internal
+{
+ ///
+ /// This class is responsible for managing DynamicAssembly instance creation taking into account
+ /// AssemblyLoadContext when running under .NET.
+ ///
+ internal class DynamicAssemblyFactory
+ {
+ private readonly string moduleName;
+
+ private readonly Lazy singletonAssembly;
+
+#if NET
+ private readonly Dictionary alcCache = new();
+#endif
+
+ public DynamicAssemblyFactory(string moduleName)
+ {
+ this.moduleName = moduleName;
+ this.singletonAssembly = new Lazy(() => new DynamicAssembly(this.moduleName));
+ }
+
+#if NET
+ public DynamicAssembly GetDynamicAssembly(Type? type)
+ {
+ if (type is null || AssemblyLoadContext.GetLoadContext(type.Assembly) is not AssemblyLoadContext loadContext)
+ {
+ return this.singletonAssembly.Value;
+ }
+ else
+ {
+ DynamicAssembly? assembly = null;
+ lock (this.alcCache)
+ {
+ if (!this.alcCache.TryGetValue(loadContext, out assembly))
+ {
+ assembly = new DynamicAssembly(this.moduleName);
+ this.alcCache[loadContext] = assembly;
+ }
+
+ return assembly;
+ }
+ }
+ }
+#else
+ public DynamicAssembly GetDynamicAssembly(Type? type)
+ {
+ return this.singletonAssembly.Value;
+ }
+#endif
+ }
+}
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs
index 2d082f9f7..89036c12e 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs
@@ -134,62 +134,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.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs
index 1e6627029..07690fd8e 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs
@@ -17,12 +17,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])
@@ -30,13 +38,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])
@@ -44,13 +52,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])
@@ -59,16 +67,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])
@@ -76,13 +92,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])
@@ -90,13 +106,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])
@@ -105,13 +121,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])
@@ -119,13 +135,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])
@@ -134,13 +150,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])
@@ -149,13 +165,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])
@@ -164,7 +180,7 @@ public static unsafe void WriteRaw7(ref MessagePackWriter writer, 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.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs
index 22d39b25d..23a40ddca 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe32.Dirty.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs
index ba10e68d2..f807ef17e 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/LZ4/LZ4Codec.Unsafe64.Dirty.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs
index d2c630d73..5a38fccd6 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs
@@ -154,81 +154,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 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 MessagePackCode.Map16:
- case MessagePackCode.Map32:
- return this.TrySkipNextMap();
- case MessagePackCode.Array16:
- case MessagePackCode.Array32:
- return this.TrySkipNextArray();
- case MessagePackCode.Str8:
- case MessagePackCode.Str16:
- case MessagePackCode.Str32:
- return this.TryGetStringLengthInBytes(out int 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:
- if ((code >= MessagePackCode.MinNegativeFixInt && code <= MessagePackCode.MaxNegativeFixInt) ||
- (code >= MessagePackCode.MinFixInt && code <= MessagePackCode.MaxFixInt))
- {
- return this.reader.TryAdvance(1);
- }
+ remainingStructures--;
+ byte code = this.NextCode;
+ switch (code)
+ {
+ case byte x when (x >= MessagePackCode.MinNegativeFixInt && x <= MessagePackCode.MaxNegativeFixInt) || (x >= MessagePackCode.MinFixInt && x <= MessagePackCode.MaxFixInt):
+ case MessagePackCode.Nil:
+ case MessagePackCode.True:
+ case MessagePackCode.False:
+ if (!this.reader.TryAdvance(1))
+ {
+ return false;
+ }
- if (code >= MessagePackCode.MinFixMap && code <= MessagePackCode.MaxFixMap)
- {
- return this.TrySkipNextMap();
- }
+ break;
+ case MessagePackCode.Int8:
+ case MessagePackCode.UInt8:
+ if (!this.reader.TryAdvance(2))
+ {
+ return false;
+ }
- if (code >= MessagePackCode.MinFixArray && code <= MessagePackCode.MaxFixArray)
- {
- return this.TrySkipNextArray();
- }
+ break;
+ case MessagePackCode.Int16:
+ case MessagePackCode.UInt16:
+ if (!this.reader.TryAdvance(3))
+ {
+ return false;
+ }
- if (code >= MessagePackCode.MinFixStr && code <= MessagePackCode.MaxFixStr)
- {
- return this.TryGetStringLengthInBytes(out length) && this.reader.TryAdvance(length);
- }
+ break;
+ case MessagePackCode.Int32:
+ case MessagePackCode.UInt32:
+ case MessagePackCode.Float32:
+ if (!this.reader.TryAdvance(5))
+ {
+ return false;
+ }
- // 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);
+ break;
+ case MessagePackCode.Int64:
+ case MessagePackCode.UInt64:
+ case MessagePackCode.Float64:
+ if (!this.reader.TryAdvance(9))
+ {
+ return false;
+ }
+
+ break;
+ case byte x when x >= MessagePackCode.MinFixMap && x <= MessagePackCode.MaxFixMap:
+ 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 x >= MessagePackCode.MinFixArray && x <= MessagePackCode.MaxFixArray:
+ case MessagePackCode.Array16:
+ case MessagePackCode.Array32:
+ if (!this.TryReadArrayHeader(out count))
+ {
+ return false;
+ }
+
+ remainingStructures = checked(remainingStructures + count);
+ break;
+ case byte x when x >= MessagePackCode.MinFixStr && x <= MessagePackCode.MaxFixStr:
+ case MessagePackCode.Str8:
+ case MessagePackCode.Str16:
+ case MessagePackCode.Str32:
+ if (!this.TryGetStringLengthInBytes(out int 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;
}
///
@@ -390,7 +432,7 @@ public int ReadMapHeader()
// Protect against corrupted or mischievious 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;
}
@@ -1130,22 +1172,5 @@ private string ReadStringSlow(int byteLength)
ArrayPool.Shared.Return(charArray);
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.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs
index 3a6715299..9d8a71d5c 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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 all protections. Useful for deserializing fully-trusted and valid msgpack sequences.
///
@@ -31,6 +33,7 @@ public class MessagePackSecurity
{
HashCollisionResistant = true,
MaximumObjectGraphDepth = 500,
+ MaximumDecompressedSize = DefaultUntrustedDataMaximumDecompressedSize,
};
private static readonly SipHash Hash = new();
@@ -57,6 +60,7 @@ protected MessagePackSecurity(MessagePackSecurity copyFrom)
this.HashCollisionResistant = copyFrom.HashCollisionResistant;
this.MaximumObjectGraphDepth = copyFrom.MaximumObjectGraphDepth;
+ this.MaximumDecompressedSize = copyFrom.MaximumDecompressedSize;
}
///
@@ -81,6 +85,12 @@ protected MessagePackSecurity(MessagePackSecurity copyFrom)
///
public int MaximumObjectGraphDepth { get; private set; } = int.MaxValue;
+ ///
+ /// 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.
///
@@ -98,6 +108,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.
///
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs
index b9d02ac62..baf8a4e92 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs
@@ -89,7 +89,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)
{
@@ -184,6 +184,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())
@@ -193,11 +198,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.
@@ -210,11 +217,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);
@@ -270,6 +279,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;
@@ -393,46 +410,54 @@ private static void ToJsonCore(ref MessagePackReader reader, TextWriter writer,
#if !UNITY_2018_3_OR_NEWER
else if (extHeader.TypeCode == ThisLibraryExtensionTypeCodes.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--;
}
}
#endif
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs
index a75917b0b..8d11b3122 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs
@@ -234,7 +234,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);
@@ -482,7 +482,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)
{
@@ -504,6 +504,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.");
@@ -530,6 +531,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();
@@ -539,6 +541,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.");
@@ -559,6 +563,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.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs
index f750f93df..8c660f464 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicEnumResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicEnumResolver.cs
index f11be19e2..c9ce48e0c 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicEnumResolver.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicEnumResolver.cs
@@ -25,7 +25,7 @@ public sealed class DynamicEnumResolver : IFormatterResolver
private const string ModuleName = "MessagePack.Resolvers.DynamicEnumResolver";
- private static readonly Lazy DynamicAssembly;
+ private static readonly DynamicAssemblyFactory DynamicAssemblyFactory;
private static int nameSequence = 0;
@@ -35,13 +35,13 @@ private DynamicEnumResolver()
static DynamicEnumResolver()
{
- DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName));
+ DynamicAssemblyFactory = new DynamicAssemblyFactory(ModuleName);
}
#if NETFRAMEWORK
internal AssemblyBuilder Save()
{
- return DynamicAssembly.Value.Save();
+ return DynamicAssemblyFactory.GetDynamicAssembly(type: null).Save();
}
#endif
@@ -95,7 +95,7 @@ private static TypeInfo BuildType(Type enumType)
{
using (MonoProtection.EnterRefEmitLock())
{
- TypeBuilder typeBuilder = DynamicAssembly.Value.DefineType("MessagePack.Formatters." + enumType.FullName!.Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
+ TypeBuilder typeBuilder = DynamicAssemblyFactory.GetDynamicAssembly(enumType).DefineType("MessagePack.Formatters." + enumType.FullName!.Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
// void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
{
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicObjectResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicObjectResolver.cs
index 6769e0ac6..e05283e88 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicObjectResolver.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicObjectResolver.cs
@@ -38,13 +38,13 @@ public sealed class DynamicObjectResolver : IFormatterResolver
///
public static readonly MessagePackSerializerOptions Options;
- internal static readonly Lazy DynamicAssembly;
+ internal static readonly DynamicAssemblyFactory DynamicAssemblyFactory;
static DynamicObjectResolver()
{
Instance = new DynamicObjectResolver();
Options = new MessagePackSerializerOptions(Instance);
- DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName));
+ DynamicAssemblyFactory = new DynamicAssemblyFactory(ModuleName);
}
private DynamicObjectResolver()
@@ -54,7 +54,7 @@ private DynamicObjectResolver()
#if NETFRAMEWORK
internal AssemblyBuilder Save()
{
- return DynamicAssembly.Value.Save();
+ return DynamicAssemblyFactory.GetDynamicAssembly(type: null).Save();
}
#endif
@@ -99,7 +99,7 @@ static FormatterCache()
TypeInfo? formatterTypeInfo;
try
{
- formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssembly.Value, typeof(T), false, false);
+ formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssemblyFactory.GetDynamicAssembly(typeof(T)), typeof(T), false, false);
}
catch (InitAccessorInGenericClassNotSupportedException)
{
@@ -181,7 +181,7 @@ public sealed class DynamicContractlessObjectResolver : IFormatterResolver
private const string ModuleName = "MessagePack.Resolvers.DynamicContractlessObjectResolver";
- private static readonly Lazy DynamicAssembly;
+ private static readonly DynamicAssemblyFactory DynamicAssemblyFactory;
private DynamicContractlessObjectResolver()
{
@@ -189,13 +189,13 @@ private DynamicContractlessObjectResolver()
static DynamicContractlessObjectResolver()
{
- DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName));
+ DynamicAssemblyFactory = new DynamicAssemblyFactory(ModuleName);
}
#if NETFRAMEWORK
internal AssemblyBuilder Save()
{
- return DynamicAssembly.Value.Save();
+ return DynamicAssemblyFactory.GetDynamicAssembly(type: null).Save();
}
#endif
@@ -242,7 +242,7 @@ static FormatterCache()
return;
}
- TypeInfo? formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssembly.Value, typeof(T), true, true);
+ TypeInfo? formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssemblyFactory.GetDynamicAssembly(typeof(T)), typeof(T), true, true);
if (formatterTypeInfo == null)
{
return;
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs
index 90e26a755..3f321b787 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs
@@ -37,7 +37,8 @@ public sealed class DynamicUnionResolver : IFormatterResolver
///
public static readonly MessagePackSerializerOptions Options;
- private static readonly Lazy DynamicAssembly;
+ private static readonly DynamicAssemblyFactory DynamicAssemblyFactory;
+
#if !UNITY_2018_3_OR_NEWER
private static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+", RegexOptions.Compiled);
#else
@@ -50,7 +51,7 @@ static DynamicUnionResolver()
{
Instance = new DynamicUnionResolver();
Options = new MessagePackSerializerOptions(Instance);
- DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName));
+ DynamicAssemblyFactory = new DynamicAssemblyFactory(ModuleName);
}
private DynamicUnionResolver()
@@ -60,7 +61,7 @@ private DynamicUnionResolver()
#if NETFRAMEWORK
internal AssemblyBuilder Save()
{
- return DynamicAssembly.Value.Save();
+ return DynamicAssemblyFactory.GetDynamicAssembly(type: null).Save();
}
#endif
@@ -138,7 +139,7 @@ static FormatterCache()
Type formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type);
using (MonoProtection.EnterRefEmitLock())
{
- TypeBuilder typeBuilder = DynamicAssembly.Value.DefineType("MessagePack.Formatters." + SubtractFullNameRegex.Replace(type.FullName!, string.Empty).Replace(".", "_") + "Formatter" + +Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
+ TypeBuilder typeBuilder = DynamicAssemblyFactory.GetDynamicAssembly(type).DefineType("MessagePack.Formatters." + SubtractFullNameRegex.Replace(type.FullName!, string.Empty).Replace(".", "_") + "Formatter" + +Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
FieldBuilder? typeToKeyAndJumpMap = null; // Dictionary>
FieldBuilder? keyToJumpMap = null; // Dictionary
@@ -330,6 +331,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);
@@ -338,7 +347,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);
@@ -405,6 +413,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);
}
@@ -429,6 +445,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.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs
index ffc7b149c..11107efac 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/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