-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use PayloadReader in System.Resources.Extensions #102379
Conversation
…e types internal so they can be included as links from other project
….Resources.Extensions.BinaryFormat
This reverts commit 6354a91.
…xception and the stream is seekable
@JeremyKuhne I am not sure if you want to review all the files (you already did that in dotnet/winforms#11341). In case you don't, you can simply review the following commits:
What is still on my TODO list is to move the tests from https://github.com/dotnet/winforms/tree/main/src/System.Private.Windows.Core/tests/BinaryFormatTests. It will be another commit with no product code changes (only copy paste of the tests and maybe some minor MSBuild changes) |
} | ||
|
||
[RequiresUnreferencedCode("Calls System.Reflection.Assembly.GetType(String, Boolean, Boolean)")] | ||
private static Type? GetSimplyNamedTypeFromAssembly(Assembly assembly, TypeName typeName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is effectively a straight copy of the existing BinaryFormatter
logic. All catch blocks in this class match the existing behavior.
{ | ||
PendingSerializationInfo? pending = _pendingSerializationInfo.Dequeue(); | ||
|
||
// Using pendingCount to only requeue on the first pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't found a need to requeue multiple times. One could, however, make this a bit more complicated and keep requeuing as long as the count continues to go down.
// Everything has to start with a header | ||
var header = (SerializedStreamHeaderRecord)ReadNext(reader, recordMap, AllowedRecordTypes.SerializedStreamHeader, options, out _); | ||
// and can be followed by any Object, BinaryLibrary and a MessageEnd. | ||
const AllowedRecordTypes allowed = AllowedRecordTypes.AnyObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the record type checks prevent any observably bad behavior? I worry a little bit about things ending up in orders that don't match the constraints of the spec, but it otherwise being ok.
...s/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs
Outdated
Show resolved
Hide resolved
...ystem.Resources.Extensions/src/BinaryFormat/Deserializer/ClassRecordFieldInfoDeserializer.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...ntime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs
Outdated
Show resolved
Hide resolved
...Format/src/System/Runtime/Serialization/BinaryFormat/RectangularOrCustomOffsetArrayRecord.cs
Outdated
Show resolved
Hide resolved
...ialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/SerializationRecord.cs
Outdated
Show resolved
Hide resolved
...time.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArrayRecord.cs
Outdated
Show resolved
Hide resolved
...time.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArrayRecord.cs
Outdated
Show resolved
Hide resolved
...ion.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArraySinglePrimitiveRecord.cs
Outdated
Show resolved
Hide resolved
...ion.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArraySinglePrimitiveRecord.cs
Outdated
Show resolved
Hide resolved
...ion.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArraySinglePrimitiveRecord.cs
Outdated
Show resolved
Hide resolved
|
||
private static List<T> DecodePrimitiveTypesToList(BinaryReader reader, int count) | ||
{ | ||
List<T> values = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will grow from 0 to 4 to 8 to ...
The 99% case for large arrays will be bytes, and that is covered for most inputs by the "if I have that much data remaining, just read it" above. But large arrays of TimeSpan (or int when not on new.NET) are going to have to go through many reallocs.
The easiest, of course, is
List<T> values = []; | |
List<T> values = new List<T>(count); |
If the threat model indicates that this puts too much power in the hands of the payload, then it needs to change to a page based allocation and a custom type.
For expediency, I recommend just pre-sizing the list and taking this as an issue to follow up on later (as part of, or prior to, the threat model). As current usage is limited to resources, it's a fine thing to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list is not pre-sized on purpose: adamsitnik/SafePayloadReader#4
Since the code for decoding is already optimized for .NET Core, I am going to add the optimization for other TFMs
...untime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ClassInfo.cs
Outdated
Show resolved
Hide resolved
...on.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ObjectNullMultiple256Record.cs
Outdated
Show resolved
Hide resolved
...ation.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ObjectNullMultipleRecord.cs
Outdated
Show resolved
Hide resolved
...ialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/PrimitiveTypeRecord.cs
Outdated
Show resolved
Hide resolved
FYI test failure:
|
src/libraries/System.Runtime.Serialization.BinaryFormat/tests/EdgeCaseTests.cs
Show resolved
Hide resolved
Co-authored-by: Jeremy Barton <[email protected]>
- prefer NET over NETCOREAPP when writing #if defines - add ConditionalTheory to make the SkipTestException actually work - ObjectNullMultiple must not allow for 0 inputs - preallocate the List<T> for TFMs other than .NET
- ensure that reading arrays of primitive types is as fast as possible when there is enough data in the stream, throw when there is not, fall back to slow path when we don't know - make GetArray reuse the previously created instance - and provide tests for all of that
…tes as what is stored in the test source code. The blobs may not be identical (the output is not deterministic), but still valid.
High level overview.
PayloadReader
related code was moved toSystem.Runtime.Serialization.BinaryFormat
library, the library is marked withIsPackable
set to false so we don't publish any NuGet package by accident. Other projects reference it's code via links (to ensure nothing relies on the project before the API gets approved).System.Resources.Extensions
is now using simplified deserializer by default.BinaryFormatter
by settingSystem.Resources.Extensions.UseBinaryFormatter
app context switch.System.Resources.Extensions.BinaryFormat.Tests
and actualBinaryFormatter
test project as I assumed the latter is soon going to be removed from this repo.