Skip to content

Commit 129239b

Browse files
committed
Defend against stack overflow from deeply nested object graphs in msgpack
1 parent 6f0ec18 commit 129239b

27 files changed

+2798
-2202
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,20 @@ public class FileInfoFormatter<T> : IMessagePackFormatter<FileInfo>
809809
return null;
810810
}
811811

812-
var path = MessagePackBinary.ReadString(bytes, offset, out readSize);
813-
return new FileInfo(path);
812+
using (MessagePackSecurity.DepthStep())
813+
{
814+
var path = MessagePackBinary.ReadString(bytes, offset, out readSize);
815+
return new FileInfo(path);
816+
}
814817
}
815818
}
816819
```
817820

821+
The `using (MessagePackSecurity.DepthStep())` block provides a level of security while deserializing untrusted data
822+
that might otherwise be able to execute a denial of service attack by sending messagepack data that would
823+
deserialize into a very deep object graph leading to a `StackOverflowException` that would crash the process.
824+
This block should surround the bulk of any `IMessagePackFormatter<T>.Deserialize` method.
825+
818826
Created formatter needs to register to `IFormatterResolver`. Please see [Extension Point section](https://github.com/neuecc/MessagePack-CSharp#extension-pointiformatterresolver).
819827

820828
You can see many other samples from [builtin formatters](https://github.com/neuecc/MessagePack-CSharp/tree/master/src/MessagePack/Formatters).

sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@
7474
<Compile Include="..\..\src\MessagePack\StringEncoding.cs">
7575
<Link>Code\StringEncoding.cs</Link>
7676
</Compile>
77+
<Compile Include="..\..\src\MessagePack\MessagePackSecurity.cs">
78+
<Link>Code\MessagePackSecurity.cs</Link>
79+
</Compile>
80+
<Compile Include="..\..\src\MessagePack\HashCode.cs">
81+
<Link>Code\HashCode.cs</Link>
82+
</Compile>
83+
<Compile Include="..\..\src\MessagePack\BitOperations.cs">
84+
<Link>Code\BitOperations.cs</Link>
85+
</Compile>
86+
<Compile Include="..\..\src\MessagePack\Internal\ThreadsafeTypeKeyHashTable.cs">
87+
<Link>Code\ThreadsafeTypeKeyHashTable.cs</Link>
88+
</Compile>
7789
<Compile Include="..\SharedData\Class1.cs">
7890
<Link>Class1.cs</Link>
7991
</Compile>

sandbox/DynamicCodeDumper/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static void Main(string[] args)
3636
//DynamicObjectResolver.Instance.GetFormatter<SimpleStringKeyData>();
3737
//DynamicObjectResolver.Instance.GetFormatter<SimpleStringKeyData2>();
3838
//DynamicObjectResolver.Instance.GetFormatter<StringKeySerializerTarget>();
39-
//DynamicObjectResolver.Instance.GetFormatter<LongestString>();
39+
DynamicObjectResolver.Instance.GetFormatter<LongestString>();
4040
var f = DynamicObjectResolverAllowPrivate.Instance.GetFormatter<MyClass>();
4141
//DynamicObjectResolver.Instance.GetFormatter<StringKeySerializerTargetBinary>();
4242
//DynamicObjectResolver.Instance.GetFormatter<Callback1>();

sandbox/Sandbox/Generated.cs

Lines changed: 1811 additions & 1646 deletions
Large diffs are not rendered by default.

src/MessagePack.ImmutableCollection/Formatters.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@ public ImmutableArray<T> Deserialize(byte[] bytes, int offset, IFormatterResolve
4545
offset += readSize;
4646

4747
var builder = ImmutableArray.CreateBuilder<T>(len);
48-
for (int i = 0; i < len; i++)
48+
using (MessagePackSecurity.DepthStep())
4949
{
50-
builder.Add(formatter.Deserialize(bytes, offset, formatterResolver, out readSize));
51-
offset += readSize;
50+
for (int i = 0; i < len; i++)
51+
{
52+
builder.Add(formatter.Deserialize(bytes, offset, formatterResolver, out readSize));
53+
offset += readSize;
54+
}
5255
}
56+
5357
readSize = offset - startOffset;
5458

5559
return builder.ToImmutable();

src/MessagePack.ReactiveProperty/Formatters.cs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,15 @@ public ReactiveProperty<T> Deserialize(byte[] bytes, int offset, IFormatterResol
146146

147147
var scheduler = ReactivePropertySchedulerMapper.GetScheduler(schedulerId);
148148

149-
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
150-
offset += readSize;
149+
using (MessagePackSecurity.DepthStep())
150+
{
151+
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
152+
offset += readSize;
151153

152-
readSize = offset - startOffset;
154+
readSize = offset - startOffset;
153155

154-
return new ReactiveProperty<T>(scheduler, v, mode);
156+
return new ReactiveProperty<T>(scheduler, v, mode);
157+
}
155158
}
156159

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

190-
switch (length)
191-
{
192-
case 2:
193-
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
194-
case 3:
195-
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
196-
default:
197-
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
193+
using (MessagePackSecurity.DepthStep())
194+
{
195+
switch (length)
196+
{
197+
case 2:
198+
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
199+
case 3:
200+
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
201+
default:
202+
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
203+
}
198204
}
199205
}
200206
}
@@ -229,14 +235,17 @@ public IReadOnlyReactiveProperty<T> Deserialize(byte[] bytes, int offset, IForma
229235
{
230236
var length = MessagePackBinary.ReadArrayHeader(bytes, offset, out readSize);
231237

232-
switch (length)
233-
{
234-
case 2:
235-
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
236-
case 3:
237-
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
238-
default:
239-
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
238+
using (MessagePackSecurity.DepthStep())
239+
{
240+
switch (length)
241+
{
242+
case 2:
243+
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactivePropertySlim<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
244+
case 3:
245+
return ReactivePropertyResolver.Instance.GetFormatterWithVerify<ReactiveProperty<T>>().Deserialize(bytes, offset, formatterResolver, out readSize);
246+
default:
247+
throw new InvalidOperationException("Invalid ReactiveProperty or ReactivePropertySlim data.");
248+
}
240249
}
241250
}
242251
}
@@ -350,14 +359,16 @@ public ReactivePropertySlim<T> Deserialize(byte[] bytes, int offset, IFormatterR
350359
var mode = (ReactivePropertyMode)MessagePackBinary.ReadInt32(bytes, offset, out readSize);
351360
offset += readSize;
352361

353-
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
354-
offset += readSize;
362+
using (MessagePackSecurity.DepthStep())
363+
{
364+
var v = formatterResolver.GetFormatterWithVerify<T>().Deserialize(bytes, offset, formatterResolver, out readSize);
365+
offset += readSize;
355366

356-
readSize = offset - startOffset;
367+
readSize = offset - startOffset;
357368

358-
return new ReactivePropertySlim<T>(v, mode);
369+
return new ReactivePropertySlim<T>(v, mode);
370+
}
359371
}
360-
361372
}
362373
}
363374
}

0 commit comments

Comments
 (0)