Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Add back support for deserialization of BinaryFormatted resources #20907

Merged
merged 3 commits into from
Nov 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/System.Private.CoreLib/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,9 @@
<data name="BadImageFormat_ParameterSignatureMismatch" xml:space="preserve">
<value>The parameters and the signature of the method don't match.</value>
</data>
<data name="BadImageFormat_ResType_SerBlobMismatch" xml:space="preserve">
<value>The type serialized in the .resources file was not the same type that the .resources file said it contained. Expected '{0}' but read '{1}'.</value>
</data>
<data name="BadImageFormat_ResourceDataLengthInvalid" xml:space="preserve">
<value>Corrupt .resources file. The specified data length '{0}' is not a valid position in the stream.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace System.Resources
using System.Runtime.Versioning;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Threading;

// Provides the default implementation of IResourceReader, reading
// .resources file from the system default binary format. This class
Expand Down Expand Up @@ -96,7 +97,16 @@ public sealed class ResourceReader : IResourceReader
private unsafe int* _namePositionsPtr; // If we're using UnmanagedMemoryStream
private Type[] _typeTable; // Lazy array of Types for resource values.
private int[] _typeNamePositions; // To delay initialize type table
private int _numResources; // Num of resources files, in case arrays aren't allocated.
private int _numResources; // Num of resources files, in case arrays aren't allocated.

private readonly bool _permitDeserialization; // can deserialize BinaryFormatted resources
private object _binaryFormatter; // binary formatter instance to use for deserializing

// statics used to dynamically call into BinaryFormatter
// When successfully located s_binaryFormatterType will point to the BinaryFormatter type
// and s_deserializeMethod will point to an unbound delegate to the deserialize method.
private static Type s_binaryFormatterType;
private static Func<object, Stream, object> s_deserializeMethod;

// We'll include a separate code path that uses UnmanagedMemoryStream to
// avoid allocating String objects and the like.
Expand Down Expand Up @@ -141,7 +151,7 @@ public ResourceReader(Stream stream)
// passing in the stream to read from and the RuntimeResourceSet's
// internal hash table (hash table of names with file offsets
// and values, coupled to this ResourceReader).
internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCache)
internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCache, bool permitDeserialization)
{
Debug.Assert(stream != null, "Need a stream!");
Debug.Assert(stream.CanRead, "Stream should be readable!");
Expand All @@ -152,6 +162,8 @@ internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCa

_ums = stream as UnmanagedMemoryStream;

_permitDeserialization = permitDeserialization;

ReadResources();
}

Expand Down Expand Up @@ -591,7 +603,7 @@ private object _LoadObjectV1(int pos)
}
else
{
throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
return DeserializeObject(typeIndex);
}
}

Expand Down Expand Up @@ -743,7 +755,61 @@ private object _LoadObjectV2(int pos, out ResourceTypeCode typeCode)
}

// Normal serialized objects
throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
return DeserializeObject(typeIndex);
}

private object DeserializeObject(int typeIndex)
{
if (!_permitDeserialization)
{
throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
}

if (_binaryFormatter == null)
{
InitializeBinaryFormatter();
}

Type type = FindType(typeIndex);

object graph = s_deserializeMethod(_binaryFormatter, _store.BaseStream);

// guard against corrupted resources
if (graph.GetType() != type)
throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResType_SerBlobMismatch, type.FullName, graph.GetType().FullName));

return graph;
}

private void InitializeBinaryFormatter()
{
LazyInitializer.EnsureInitialized(ref s_binaryFormatterType, () =>
Type.GetType("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, System.Runtime.Serialization.Formatters, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
throwOnError: true));

LazyInitializer.EnsureInitialized(ref s_deserializeMethod, () =>
{
MethodInfo binaryFormatterDeserialize = s_binaryFormatterType.GetMethod("Deserialize", new Type[] { typeof(Stream) });

// create an unbound delegate that can accept a BinaryFormatter instance as object
return (Func<object, Stream, object>)typeof(ResourceReader)
.GetMethod(nameof(CreateUntypedDelegate), BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(s_binaryFormatterType)
.Invoke(null, new object[] { binaryFormatterDeserialize });
});

_binaryFormatter = Activator.CreateInstance(s_binaryFormatterType);
}

// generic method that we specialize at runtime once we've loaded the BinaryFormatter type
// permits creating an unbound delegate so that we can avoid reflection after the initial
// lightup code completes.
private static Func<object, Stream, object> CreateUntypedDelegate<TInstance>(MethodInfo method)
{
Func<TInstance, Stream, object> typedDelegate = (Func<TInstance, Stream, object>)Delegate.CreateDelegate(typeof(Func<TInstance, Stream, object>), null, method);

return (obj, stream) => typedDelegate((TInstance)obj, stream);
}

// Reads in the header information for a .resources file. Verifies some
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ internal RuntimeResourceSet(string fileName) : base(false)
{
_resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
_defaultReader = new ResourceReader(stream, _resCache);
_defaultReader = new ResourceReader(stream, _resCache, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit permitDeserialization:false ?

Reader = _defaultReader;
}

internal RuntimeResourceSet(Stream stream) : base(false)
internal RuntimeResourceSet(Stream stream, bool permitDeserialization = false) : base(false)
{
_resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
_defaultReader = new ResourceReader(stream, _resCache);
_defaultReader = new ResourceReader(stream, _resCache, permitDeserialization);
Reader = _defaultReader;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ internal ResourceSet CreateResourceSet(Stream store, Assembly assembly)
// the abbreviated ones emitted by InternalResGen.
if (CanUseDefaultResourceClasses(readerTypeName, resSetTypeName))
{
return new RuntimeResourceSet(store);
return new RuntimeResourceSet(store, permitDeserialization: true);
}
else
{
Expand Down Expand Up @@ -276,7 +276,7 @@ internal ResourceSet CreateResourceSet(Stream store, Assembly assembly)

if (_mediator.UserResourceSet == null)
{
return new RuntimeResourceSet(store);
return new RuntimeResourceSet(store, permitDeserialization:true);
}
else
{
Expand Down