Skip to content

Commit

Permalink
Defend against stack overflow from deeply nested object graphs in msg…
Browse files Browse the repository at this point in the history
…pack
  • Loading branch information
AArnott committed Jan 14, 2020
1 parent 6f0ec18 commit 129239b
Show file tree
Hide file tree
Showing 27 changed files with 2,798 additions and 2,202 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -809,12 +809,20 @@ public class FileInfoFormatter<T> : IMessagePackFormatter<FileInfo>
return null;
}

var path = MessagePackBinary.ReadString(bytes, offset, out readSize);
return new FileInfo(path);
using (MessagePackSecurity.DepthStep())
{
var path = MessagePackBinary.ReadString(bytes, offset, out readSize);
return new FileInfo(path);
}
}
}
```

The `using (MessagePackSecurity.DepthStep())` block provides a level of security while deserializing untrusted data
that might otherwise be able to execute a denial of service attack by sending messagepack data that would
deserialize into a very deep object graph leading to a `StackOverflowException` that would crash the process.
This block should surround the bulk of any `IMessagePackFormatter<T>.Deserialize` method.

Created formatter needs to register to `IFormatterResolver`. Please see [Extension Point section](https://github.com/neuecc/MessagePack-CSharp#extension-pointiformatterresolver).

You can see many other samples from [builtin formatters](https://github.com/neuecc/MessagePack-CSharp/tree/master/src/MessagePack/Formatters).
Expand Down
12 changes: 12 additions & 0 deletions sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@
<Compile Include="..\..\src\MessagePack\StringEncoding.cs">
<Link>Code\StringEncoding.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\MessagePackSecurity.cs">
<Link>Code\MessagePackSecurity.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\HashCode.cs">
<Link>Code\HashCode.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\BitOperations.cs">
<Link>Code\BitOperations.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\Internal\ThreadsafeTypeKeyHashTable.cs">
<Link>Code\ThreadsafeTypeKeyHashTable.cs</Link>
</Compile>
<Compile Include="..\SharedData\Class1.cs">
<Link>Class1.cs</Link>
</Compile>
Expand Down
2 changes: 1 addition & 1 deletion sandbox/DynamicCodeDumper/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static void Main(string[] args)
//DynamicObjectResolver.Instance.GetFormatter<SimpleStringKeyData>();
//DynamicObjectResolver.Instance.GetFormatter<SimpleStringKeyData2>();
//DynamicObjectResolver.Instance.GetFormatter<StringKeySerializerTarget>();
//DynamicObjectResolver.Instance.GetFormatter<LongestString>();
DynamicObjectResolver.Instance.GetFormatter<LongestString>();
var f = DynamicObjectResolverAllowPrivate.Instance.GetFormatter<MyClass>();
//DynamicObjectResolver.Instance.GetFormatter<StringKeySerializerTargetBinary>();
//DynamicObjectResolver.Instance.GetFormatter<Callback1>();
Expand Down
3,457 changes: 1,811 additions & 1,646 deletions sandbox/Sandbox/Generated.cs

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/MessagePack.ImmutableCollection/Formatters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ public ImmutableArray<T> Deserialize(byte[] bytes, int offset, IFormatterResolve
offset += readSize;

var builder = ImmutableArray.CreateBuilder<T>(len);
for (int i = 0; i < len; i++)
using (MessagePackSecurity.DepthStep())
{
builder.Add(formatter.Deserialize(bytes, offset, formatterResolver, out readSize));
offset += readSize;
for (int i = 0; i < len; i++)
{
builder.Add(formatter.Deserialize(bytes, offset, formatterResolver, out readSize));
offset += readSize;
}
}

readSize = offset - startOffset;

return builder.ToImmutable();
Expand Down
61 changes: 36 additions & 25 deletions src/MessagePack.ReactiveProperty/Formatters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,15 @@ public ReactiveProperty<T> Deserialize(byte[] bytes, int offset, IFormatterResol

var scheduler = ReactivePropertySchedulerMapper.GetScheduler(schedulerId);

var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
offset += readSize;
using (MessagePackSecurity.DepthStep())
{
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
offset += readSize;

readSize = offset - startOffset;
readSize = offset - startOffset;

return new ReactiveProperty<T>(scheduler, v, mode);
return new ReactiveProperty<T>(scheduler, v, mode);
}
}

}
Expand Down Expand Up @@ -187,14 +190,17 @@ public IReactiveProperty<T> Deserialize(byte[] bytes, int offset, IFormatterReso
{
var length = MessagePackBinary.ReadArrayHeader(bytes, offset, out readSize);

switch (length)
{
case 2:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
case 3:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
default:
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
using (MessagePackSecurity.DepthStep())
{
switch (length)
{
case 2:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
case 3:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
default:
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
}
}
}
}
Expand Down Expand Up @@ -229,14 +235,17 @@ public IReadOnlyReactiveProperty<T> Deserialize(byte[] bytes, int offset, IForma
{
var length = MessagePackBinary.ReadArrayHeader(bytes, offset, out readSize);

switch (length)
{
case 2:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
case 3:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
default:
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
using (MessagePackSecurity.DepthStep())
{
switch (length)
{
case 2:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
case 3:
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
default:
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
}
}
}
}
Expand Down Expand Up @@ -350,14 +359,16 @@ public ReactivePropertySlim<T> Deserialize(byte[] bytes, int offset, IFormatterR
var mode = (ReactivePropertyMode)MessagePackBinary.ReadInt32(bytes, offset, out readSize);
offset += readSize;

var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
offset += readSize;
using (MessagePackSecurity.DepthStep())
{
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
offset += readSize;

readSize = offset - startOffset;
readSize = offset - startOffset;

return new ReactivePropertySlim<T>(v, mode);
return new ReactivePropertySlim<T>(v, mode);
}
}

}
}
}
Loading

0 comments on commit 129239b

Please sign in to comment.