Applications are given the option of disabling BinaryFormatter.Serialize and BinaryFormatter.Deserialize, meaning they'll throw NotSupportedException if anybody tries calling them. WASM projects always disable these APIs via a hardcoded mechanism; there is no re-enablement mechanism for those projects.
This commit only introduces the switch. It does not disable it in any project types by default. (Exception: wasm, as mentioned above.) The related PR https://github.com/dotnet/sdk/pull/12434 causes aspnet 5.0+ applications to disable BinaryFormatter functionality by default, but apps can re-enable if needed.
| MSBuild Property Name | AppContext Setting | Description |
|-|-|-|
| DebuggerSupport | System.Diagnostics.Debugger.IsSupported | Any dependency that enables better debugging experience to be trimmed when set to false |
-| EnableUnsafeUTF7Encoding | System.Text.Encoding.EnableUnsafeUTF7Encoding | Insecure UTF-7 encoding is trimmed when set to false |
+| EnableUnsafeUTF7Encoding | System.Text.Encoding.EnableUnsafeUTF7Encoding | Insecure UTF-7 encoding is trimmed when set to false |
+| EnableUnsafeBinaryFormatterSerialization | System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization | BinaryFormatter serialization support is trimmed when set to false |
| EventSourceSupport | System.Diagnostics.Tracing.EventSource.IsSupported | Any EventSource related code or logic is trimmed when set to false |
| InvariantGlobalization | System.Globalization.Invariant | All globalization specific code and data is trimmed when set to true |
| UseSystemResourceKeys | System.Resources.UseSystemResourceKeys | Any localizable resources for system assemblies is trimmed when set to true |
return true;
}
+ if (switchName == "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")
+ {
+ return true;
+ }
+
return false;
}
}
{
public abstract partial class IEnumerable_Generic_Tests<T> : TestBase<T>
{
- [Theory]
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
[MemberData(nameof(ValidCollectionSizes))]
public void IGenericSharedAPI_SerializeDeserialize(int count)
{
{
public abstract partial class IEnumerable_NonGeneric_Tests : TestBase
{
- [Theory]
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
[MemberData(nameof(ValidCollectionSizes))]
public void IGenericSharedAPI_SerializeDeserialize(int count)
{
{
public abstract partial class ComparersGenericTests<T>
{
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void EqualityComparer_SerializationRoundtrip()
{
var bf = new BinaryFormatter();
return dict;
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void ComparerSerialization()
{
// Strings switch between randomized and non-randomized comparers,
#region Serialization
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void ComparerSerialization()
{
// Strings switch between randomized and non-randomized comparers,
Assert.Same(value, ex.Value);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
Assert.Null(exception.ParamName);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
AssertExtensions.Throws<ArgumentNullException, NullReferenceException>("enumClass", () => new InvalidEnumArgumentException("argumentName", 1, null));
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
Assert.Null(exception.InnerException);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
Assert.Equal(message, exception.Message);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
Assert.Equal(message, exception.Message);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var stream = new MemoryStream())
<type fullname="System.LocalAppContextSwitches">
<method signature="System.Boolean get_EnableUnsafeUTF7Encoding()" body="stub" value="false" feature="System.Text.Encoding.EnableUnsafeUTF7Encoding" featurevalue="false" />
</type>
+ <type fullname="System.Resources.ResourceReader">
+ <method signature="System.Boolean InitializeBinaryFormatter()" body="stub" value="false" feature="System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization" featurevalue="false" />
+ </type>
</assembly>
</linker>
<data name="Arg_MustBeHalf" xml:space="preserve">
<value>Object must be of type Half.</value>
</data>
+ <data name="BinaryFormatter_SerializationDisallowed" xml:space="preserve">
+ <value>BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.</value>
+ </data>
</root>
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\SafeSerializationEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\SerializationException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\SerializationInfo.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\SerializationInfo.SerializationGuard.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\SerializationInfoEnumerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\StreamingContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\ComponentGuaranteesAttribute.cs" />
using System.Diagnostics;
using System.IO;
using System.Reflection;
+using System.Runtime.Serialization;
using System.Text;
using System.Threading;
if (_binaryFormatter == null)
{
- InitializeBinaryFormatter();
+ if (!InitializeBinaryFormatter())
+ {
+ // The linker trimmed away the BinaryFormatter implementation and we can't call into it.
+ // We'll throw an exception with the same text that BinaryFormatter would have thrown
+ // had we been able to call into it. Keep this resource string in sync with the same
+ // resource from the Formatters assembly.
+ throw new NotSupportedException(SR.BinaryFormatter_SerializationDisallowed);
+ }
}
Type type = FindType(typeIndex);
}
// Issue https://github.com/dotnet/runtime/issues/39290 tracks finding an alternative to BinaryFormatter
- private void InitializeBinaryFormatter()
+ private bool InitializeBinaryFormatter()
{
+ // If BinaryFormatter support is disabled for the app, the linker will replace this entire
+ // method body with "return false;", skipping all reflection code below.
+
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)!);
});
_binaryFormatter = Activator.CreateInstance(s_binaryFormatterType!)!;
+
+ return true; // initialization successful
}
// generic method that we specialize at runtime once we've loaded the BinaryFormatter type
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security;
+using System.Threading;
+
+namespace System.Runtime.Serialization
+{
+ /// <summary>The structure for holding all of the data needed for object serialization and deserialization.</summary>
+ public sealed partial class SerializationInfo
+ {
+ internal static AsyncLocal<bool> AsyncDeserializationInProgress { get; } = new AsyncLocal<bool>();
+
+#if !CORECLR
+ // On AoT, assume private members are reflection blocked, so there's no further protection required
+ // for the thread's DeserializationTracker
+ [ThreadStatic]
+ private static DeserializationTracker? t_deserializationTracker;
+
+ private static DeserializationTracker GetThreadDeserializationTracker() =>
+ t_deserializationTracker ??= new DeserializationTracker();
+#endif // !CORECLR
+
+ // Returns true if deserialization is currently in progress
+ public static bool DeserializationInProgress
+ {
+#if CORECLR
+ [DynamicSecurityMethod] // Methods containing StackCrawlMark local var must be marked DynamicSecurityMethod
+#endif
+ get
+ {
+ if (AsyncDeserializationInProgress.Value)
+ {
+ return true;
+ }
+
+#if CORECLR
+ StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+ DeserializationTracker tracker = Thread.GetThreadDeserializationTracker(ref stackMark);
+#else
+ DeserializationTracker tracker = GetThreadDeserializationTracker();
+#endif
+ bool result = tracker.DeserializationInProgress;
+ return result;
+ }
+ }
+
+ // Throws a SerializationException if dangerous deserialization is currently
+ // in progress
+ public static void ThrowIfDeserializationInProgress()
+ {
+ if (DeserializationInProgress)
+ {
+ throw new SerializationException(SR.Serialization_DangerousDeserialization);
+ }
+ }
+
+ // Throws a DeserializationBlockedException if dangerous deserialization is currently
+ // in progress and the AppContext switch Switch.System.Runtime.Serialization.SerializationGuard.{switchSuffix}
+ // is not true. The value of the switch is cached in cachedValue to avoid repeated lookups:
+ // 0: No value cached
+ // 1: The switch is true
+ // -1: The switch is false
+ public static void ThrowIfDeserializationInProgress(string switchSuffix, ref int cachedValue)
+ {
+ const string SwitchPrefix = "Switch.System.Runtime.Serialization.SerializationGuard.";
+ if (switchSuffix == null)
+ {
+ throw new ArgumentNullException(nameof(switchSuffix));
+ }
+ if (string.IsNullOrWhiteSpace(switchSuffix))
+ {
+ throw new ArgumentException(SR.Argument_EmptyName, nameof(switchSuffix));
+ }
+
+ if (cachedValue == 0)
+ {
+ if (AppContext.TryGetSwitch(SwitchPrefix + switchSuffix, out bool isEnabled) && isEnabled)
+ {
+ cachedValue = 1;
+ }
+ else
+ {
+ cachedValue = -1;
+ }
+ }
+
+ if (cachedValue == 1)
+ {
+ return;
+ }
+ else if (cachedValue == -1)
+ {
+ if (DeserializationInProgress)
+ {
+ throw new SerializationException(SR.Format(SR.Serialization_DangerousDeserialization_Switch, SwitchPrefix + switchSuffix));
+ }
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(cachedValue));
+ }
+ }
+
+ // Declares that the current thread and async context have begun deserialization.
+ // In this state, if the SerializationGuard or other related AppContext switches are set,
+ // actions likely to be dangerous during deserialization, such as starting a process will be blocked.
+ // Returns a DeserializationToken that must be disposed to remove the deserialization state.
+#if CORECLR
+ [DynamicSecurityMethod] // Methods containing StackCrawlMark local var must be marked DynamicSecurityMethod
+#endif
+ public static DeserializationToken StartDeserialization()
+ {
+ if (LocalAppContextSwitches.SerializationGuard)
+ {
+#if CORECLR
+ StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+ DeserializationTracker tracker = Thread.GetThreadDeserializationTracker(ref stackMark);
+#else
+ DeserializationTracker tracker = GetThreadDeserializationTracker();
+#endif
+ if (!tracker.DeserializationInProgress)
+ {
+ lock (tracker)
+ {
+ if (!tracker.DeserializationInProgress)
+ {
+ AsyncDeserializationInProgress.Value = true;
+ tracker.DeserializationInProgress = true;
+ return new DeserializationToken(tracker);
+ }
+ }
+ }
+ }
+
+ return new DeserializationToken(null);
+ }
+ }
+}
using System.Collections.Generic;
using System.Diagnostics;
-using System.Security;
-using System.Threading;
namespace System.Runtime.Serialization
{
/// <summary>The structure for holding all of the data needed for object serialization and deserialization.</summary>
- public sealed class SerializationInfo
+ public sealed partial class SerializationInfo
{
private const int DefaultSize = 4;
private string _rootTypeAssemblyName;
private Type _rootType;
- internal static AsyncLocal<bool> AsyncDeserializationInProgress { get; } = new AsyncLocal<bool>();
-
-#if !CORECLR
- // On AoT, assume private members are reflection blocked, so there's no further protection required
- // for the thread's DeserializationTracker
- [ThreadStatic]
- private static DeserializationTracker? t_deserializationTracker;
-
- private static DeserializationTracker GetThreadDeserializationTracker() =>
- t_deserializationTracker ??= new DeserializationTracker();
-#endif // !CORECLR
-
- // Returns true if deserialization is currently in progress
- public static bool DeserializationInProgress
- {
-#if CORECLR
- [DynamicSecurityMethod] // Methods containing StackCrawlMark local var must be marked DynamicSecurityMethod
-#endif
- get
- {
- if (AsyncDeserializationInProgress.Value)
- {
- return true;
- }
-
-#if CORECLR
- StackCrawlMark stackMark = StackCrawlMark.LookForMe;
- DeserializationTracker tracker = Thread.GetThreadDeserializationTracker(ref stackMark);
-#else
- DeserializationTracker tracker = GetThreadDeserializationTracker();
-#endif
- bool result = tracker.DeserializationInProgress;
- return result;
- }
- }
-
- // Throws a SerializationException if dangerous deserialization is currently
- // in progress
- public static void ThrowIfDeserializationInProgress()
- {
- if (DeserializationInProgress)
- {
- throw new SerializationException(SR.Serialization_DangerousDeserialization);
- }
- }
-
- // Throws a DeserializationBlockedException if dangerous deserialization is currently
- // in progress and the AppContext switch Switch.System.Runtime.Serialization.SerializationGuard.{switchSuffix}
- // is not true. The value of the switch is cached in cachedValue to avoid repeated lookups:
- // 0: No value cached
- // 1: The switch is true
- // -1: The switch is false
- public static void ThrowIfDeserializationInProgress(string switchSuffix, ref int cachedValue)
- {
- const string SwitchPrefix = "Switch.System.Runtime.Serialization.SerializationGuard.";
- if (switchSuffix == null)
- {
- throw new ArgumentNullException(nameof(switchSuffix));
- }
- if (string.IsNullOrWhiteSpace(switchSuffix))
- {
- throw new ArgumentException(SR.Argument_EmptyName, nameof(switchSuffix));
- }
-
- if (cachedValue == 0)
- {
- if (AppContext.TryGetSwitch(SwitchPrefix + switchSuffix, out bool isEnabled) && isEnabled)
- {
- cachedValue = 1;
- }
- else
- {
- cachedValue = -1;
- }
- }
-
- if (cachedValue == 1)
- {
- return;
- }
- else if (cachedValue == -1)
- {
- if (DeserializationInProgress)
- {
- throw new SerializationException(SR.Format(SR.Serialization_DangerousDeserialization_Switch, SwitchPrefix + switchSuffix));
- }
- }
- else
- {
- throw new ArgumentOutOfRangeException(nameof(cachedValue));
- }
- }
-
- // Declares that the current thread and async context have begun deserialization.
- // In this state, if the SerializationGuard or other related AppContext switches are set,
- // actions likely to be dangerous during deserialization, such as starting a process will be blocked.
- // Returns a DeserializationToken that must be disposed to remove the deserialization state.
-#if CORECLR
- [DynamicSecurityMethod] // Methods containing StackCrawlMark local var must be marked DynamicSecurityMethod
-#endif
- public static DeserializationToken StartDeserialization()
- {
- if (LocalAppContextSwitches.SerializationGuard)
- {
-#if CORECLR
- StackCrawlMark stackMark = StackCrawlMark.LookForMe;
- DeserializationTracker tracker = Thread.GetThreadDeserializationTracker(ref stackMark);
-#else
- DeserializationTracker tracker = GetThreadDeserializationTracker();
-#endif
- if (!tracker.DeserializationInProgress)
- {
- lock (tracker)
- {
- if (!tracker.DeserializationInProgress)
- {
- AsyncDeserializationInProgress.Value = true;
- tracker.DeserializationInProgress = true;
- return new DeserializationToken(tracker);
- }
- }
- }
- }
-
- return new DeserializationToken(null);
- }
-
[CLSCompliant(false)]
public SerializationInfo(Type type, IFormatterConverter converter)
{
--- /dev/null
+<linker>
+ <assembly fullname="System.Runtime.Serialization.Formatters">
+ <type fullname="System.LocalAppContextSwitches">
+ <method signature="System.Boolean get_BinaryFormatterEnabled()" body="stub" value="false" feature="System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization" featurevalue="false" />
+ </type>
+ </assembly>
+</linker>
<data name="IO_EOF_ReadBeyondEOF" xml:space="preserve">
<value>Unable to read beyond the end of the stream.</value>
</data>
+ <data name="BinaryFormatter_SerializationDisallowed" xml:space="preserve">
+ <value>BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.</value>
+ </data>
</root>
<PropertyGroup>
<AssemblyName>System.Runtime.Serialization.Formatters</AssemblyName>
<RootNamespace>System.Runtime.Serialization.Formatters</RootNamespace>
- <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
+ <TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser</TargetFrameworks>
<Nullable>enable</Nullable>
+ <!-- When we replace implementations with PNSE, need to suppress some "field is never assigned to" warnings. -->
+ <NoWarn Condition="'$(TargetsBrowser)' == 'true'">$(NoWarn);CS0649</NoWarn>
</PropertyGroup>
+ <!-- ILLinker settings -->
+ <PropertyGroup>
+ <ILLinkDirectory>$(MSBuildThisFileDirectory)ILLink\</ILLinkDirectory>
+ </PropertyGroup>
+ <ItemGroup>
+ <ILLinkSubstitutionsXmls Include="$(ILLinkDirectory)ILLink.Substitutions.xml" />
+ </ItemGroup>
<ItemGroup>
<Compile Include="System.Runtime.Serialization.Formatters.TypeForwards.cs" />
<Compile Include="System\Runtime\Serialization\DeserializationEventHandler.cs" />
<Compile Include="System\Runtime\Serialization\IFormatter.cs" />
<Compile Include="System\Runtime\Serialization\ISerializationSurrogate.cs" />
<Compile Include="System\Runtime\Serialization\ISurrogateSelector.cs" />
+ <Compile Include="System\Runtime\Serialization\LocalAppContextSwitches.cs" />
+ <Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
+ <Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
+ </Compile>
<Compile Include="System\Runtime\Serialization\MemberHolder.cs" />
<Compile Include="System\Runtime\Serialization\ObjectIDGenerator.cs" />
<Compile Include="System\Runtime\Serialization\ObjectManager.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\Converter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryEnums.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
+ <Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" Condition="'$(TargetsBrowser)' != 'true'" />
+ <Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.PlatformNotSupported.cs" Condition="'$(TargetsBrowser)' == 'true'" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+
+namespace System.Runtime.Serialization.Formatters.Binary
+{
+ public sealed partial class BinaryFormatter : IFormatter
+ {
+ [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
+ public object Deserialize(Stream serializationStream)
+ {
+ // don't refactor the 'throw' into a helper method; linker will have difficulty trimming
+ if (!LocalAppContextSwitches.BinaryFormatterEnabled)
+ {
+ throw new NotSupportedException(SR.BinaryFormatter_SerializationDisallowed);
+ }
+
+ if (serializationStream == null)
+ {
+ throw new ArgumentNullException(nameof(serializationStream));
+ }
+ if (serializationStream.CanSeek && (serializationStream.Length == 0))
+ {
+ throw new SerializationException(SR.Serialization_Stream);
+ }
+
+ var formatterEnums = new InternalFE()
+ {
+ _typeFormat = _typeFormat,
+ _serializerTypeEnum = InternalSerializerTypeE.Binary,
+ _assemblyFormat = _assemblyFormat,
+ _securityLevel = _securityLevel,
+ };
+
+ var reader = new ObjectReader(serializationStream, _surrogates, _context, formatterEnums, _binder)
+ {
+ _crossAppDomainArray = _crossAppDomainArray
+ };
+ try
+ {
+ var parser = new BinaryParser(serializationStream, reader);
+ return reader.Deserialize(parser);
+ }
+ catch (SerializationException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ throw new SerializationException(SR.Serialization_CorruptedStream, e);
+ }
+ }
+
+ [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
+ public void Serialize(Stream serializationStream, object graph)
+ {
+ // don't refactor the 'throw' into a helper method; linker will have difficulty trimming
+ if (!LocalAppContextSwitches.BinaryFormatterEnabled)
+ {
+ throw new NotSupportedException(SR.BinaryFormatter_SerializationDisallowed);
+ }
+
+ if (serializationStream == null)
+ {
+ throw new ArgumentNullException(nameof(serializationStream));
+ }
+
+ var formatterEnums = new InternalFE()
+ {
+ _typeFormat = _typeFormat,
+ _serializerTypeEnum = InternalSerializerTypeE.Binary,
+ _assemblyFormat = _assemblyFormat,
+ };
+
+ var sow = new ObjectWriter(_surrogates, _context, formatterEnums, _binder);
+ BinaryFormatterWriter binaryWriter = new BinaryFormatterWriter(serializationStream, sow, _typeFormat);
+ sow.Serialize(graph, binaryWriter);
+ _crossAppDomainArray = sow._crossAppDomainArray;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+
+namespace System.Runtime.Serialization.Formatters.Binary
+{
+ public sealed partial class BinaryFormatter : IFormatter
+ {
+ [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
+ public object Deserialize(Stream serializationStream)
+ => throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationDisallowed);
+
+ [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
+ public void Serialize(Stream serializationStream, object graph)
+ => throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationDisallowed);
+ }
+}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.IO;
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
namespace System.Runtime.Serialization.Formatters.Binary
{
- public sealed class BinaryFormatter : IFormatter
+ public sealed partial class BinaryFormatter : IFormatter
{
private static readonly ConcurrentDictionary<Type, TypeInformation> s_typeNameCache = new ConcurrentDictionary<Type, TypeInformation>();
_context = context;
}
- [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
- public object Deserialize(Stream serializationStream) => Deserialize(serializationStream, true);
-
- internal object Deserialize(Stream serializationStream, bool check)
- {
- if (serializationStream == null)
- {
- throw new ArgumentNullException(nameof(serializationStream));
- }
- if (serializationStream.CanSeek && (serializationStream.Length == 0))
- {
- throw new SerializationException(SR.Serialization_Stream);
- }
-
- var formatterEnums = new InternalFE()
- {
- _typeFormat = _typeFormat,
- _serializerTypeEnum = InternalSerializerTypeE.Binary,
- _assemblyFormat = _assemblyFormat,
- _securityLevel = _securityLevel,
- };
-
- var reader = new ObjectReader(serializationStream, _surrogates, _context, formatterEnums, _binder)
- {
- _crossAppDomainArray = _crossAppDomainArray
- };
- try
- {
- var parser = new BinaryParser(serializationStream, reader);
- return reader.Deserialize(parser, check);
- }
- catch (SerializationException)
- {
- throw;
- }
- catch (Exception e)
- {
- throw new SerializationException(SR.Serialization_CorruptedStream, e);
- }
- }
-
- [Obsolete(Obsoletions.InsecureSerializationMessage, DiagnosticId = Obsoletions.InsecureSerializationDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
- public void Serialize(Stream serializationStream, object graph) =>
- Serialize(serializationStream, graph, true);
-
- internal void Serialize(Stream serializationStream, object graph, bool check)
- {
- if (serializationStream == null)
- {
- throw new ArgumentNullException(nameof(serializationStream));
- }
-
- var formatterEnums = new InternalFE()
- {
- _typeFormat = _typeFormat,
- _serializerTypeEnum = InternalSerializerTypeE.Binary,
- _assemblyFormat = _assemblyFormat,
- };
-
- var sow = new ObjectWriter(_surrogates, _context, formatterEnums, _binder);
- BinaryFormatterWriter binaryWriter = new BinaryFormatterWriter(serializationStream, sow, _typeFormat);
- sow.Serialize(graph, binaryWriter, check);
- _crossAppDomainArray = sow._crossAppDomainArray;
- }
-
internal static TypeInformation GetTypeInformation(Type type) =>
s_typeNameCache.GetOrAdd(type, t =>
{
_binder = binder;
_formatterEnums = formatterEnums;
}
- internal object Deserialize(BinaryParser serParser, bool fCheck)
+ internal object Deserialize(BinaryParser serParser)
{
if (serParser == null)
{
_objectManager = new SerializationObjectManager(context);
}
- internal void Serialize(object graph, BinaryFormatterWriter serWriter, bool fCheck)
+ internal void Serialize(object graph, BinaryFormatterWriter serWriter)
{
if (graph == null)
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static partial class LocalAppContextSwitches
+ {
+ private static int s_binaryFormatterEnabled;
+ public static bool BinaryFormatterEnabled
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", ref s_binaryFormatterEnabled);
+ }
+ }
+}
namespace System.Runtime.Serialization.Formatters.Tests
{
+ [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public partial class BinaryFormatterTests : FileCleanupTestBase
{
// On 32-bit we can't test these high inputs as they cause OutOfMemoryExceptions.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+namespace System.Runtime.Serialization.Formatters.Tests
+{
+ public static class DisableBitTests
+ {
+ // these tests only make sense on platforms with both "feature switch" and RemoteExecutor support
+ public static bool ShouldRunFullFeatureSwitchEnablementChecks => !PlatformDetection.IsNetFramework && RemoteExecutor.IsSupported;
+
+ // determines whether BinaryFormatter will always fail, regardless of config, on this platform
+ public static bool IsBinaryFormatterSuppressedOnThisPlatform => !PlatformDetection.IsBinaryFormatterSupported;
+
+ private const string EnableBinaryFormatterSwitchName = "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization";
+ private const string MoreInfoUrl = "https://aka.ms/binaryformatter";
+
+ [ConditionalFact(nameof(IsBinaryFormatterSuppressedOnThisPlatform))]
+ public static void DisabledAlwaysInBrowser()
+ {
+ // First, test serialization
+
+ MemoryStream ms = new MemoryStream();
+ BinaryFormatter bf = new BinaryFormatter();
+ var ex = Assert.Throws<PlatformNotSupportedException>(() => bf.Serialize(ms, "A string to serialize."));
+ Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL
+
+ // Then test deserialization
+
+ ex = Assert.Throws<PlatformNotSupportedException>(() => bf.Deserialize(ms));
+ Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL
+ }
+
+ [ConditionalFact(nameof(ShouldRunFullFeatureSwitchEnablementChecks))]
+ public static void DisabledThroughFeatureSwitch()
+ {
+ RemoteInvokeOptions options = new RemoteInvokeOptions();
+ options.RuntimeConfigurationOptions[EnableBinaryFormatterSwitchName] = bool.FalseString;
+
+ RemoteExecutor.Invoke(() =>
+ {
+ // First, test serialization
+
+ MemoryStream ms = new MemoryStream();
+ BinaryFormatter bf = new BinaryFormatter();
+ var ex = Assert.Throws<NotSupportedException>(() => bf.Serialize(ms, "A string to serialize."));
+ Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL
+
+ // Then test deserialization
+
+ ex = Assert.Throws<NotSupportedException>(() => bf.Deserialize(ms));
+ Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL
+ }, options).Dispose();
+ }
+ }
+}
Assert.Null(typeName);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void BindToType_AllValuesTracked()
{
var s = new MemoryStream();
namespace System.Runtime.Serialization.Formatters.Tests
{
+ [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public static class SerializationGuardTests
{
[Fact]
<ItemGroup>
<Compile Include="BinaryFormatterTestData.cs" />
<Compile Include="BinaryFormatterTests.cs" />
+ <Compile Include="DisableBitTests.cs" />
<Compile Include="EqualityExtensions.cs" />
<Compile Include="OptionalFieldAttributeTests.cs" />
<Compile Include="FormatterConverterTests.cs" />
Assert.Equal(innerException, exception.InnerException);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var memoryStream = new MemoryStream())
Assert.Equal(COR_E_SERIALIZATION, exception.HResult);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void Ctor_SerializationInfo_StreamingContext()
{
using (var memoryStream = new MemoryStream())
<type fullname="System.Runtime.CompilerServices.RuntimeFeature">
<method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="false" />
</type>
+ <type fullname="System.Resources.ResourceReader">
+ <method signature="System.Boolean InitializeBinaryFormatter()" body="stub" value="false" />
+ </type>
</assembly>
</linker>