From 4a7ac0e1f2dd20ec4424b4fccb0aba0624407a5f Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 17 Jun 2024 16:44:33 -0700 Subject: [PATCH] Move the working BinaryFormatter implementation to a NuGet package This change makes System.Runtime.Serialization.Formatters.dll build for both NetCoreAppMinimum and NetCoreAppCurrent. * The NetCoreAppCurrent version has a copy of BinaryFormatter that unconditionally throws PNSE from calls to Serialize or Deserialize. * It is included in the shared runtime * It is permanently AssemblyVersion locked to 8.1 ("bigger than 8.0, smaller than 9.0") * It is not included in the new NuGet package. * The NetCoreAppMinimum version(s) are exactly what this library was in .NET 8 * The AppContext switch is still required to use it * These TFMs are included in a resurrected System.Runtime.Serialization.Formatters nupkg * The AssemblyVersion floats with the repository (so 9.0 now, and we could stop producing the package in 10... or make a 10.0 then) --- .../TestUtilities/System/PlatformDetection.cs | 33 +++++++- .../tests/System/Data/DataSetTest.cs | 5 +- .../tests/System/Data/DataTableTest.cs | 5 +- ...urces.Extensions.BinaryFormat.Tests.csproj | 2 + .../Directory.Build.targets | 14 ++++ ...em.Runtime.Serialization.Formatters.csproj | 4 +- ...NonMobile.xml => ILLink.Substitutions.xml} | 0 .../src/PACKAGE.md | 17 ++++ .../src/Resources/Strings.resx | 3 + ...em.Runtime.Serialization.Formatters.csproj | 63 ++++++++------- .../Formatters/Binary/BinaryFormatter.Core.cs | 12 +++ ...upported.cs => BinaryFormatter.Removed.cs} | 4 +- .../Formatters/Binary/BinaryFormatter.cs | 12 --- .../Formatters/Binary/BinaryObjectReader.cs | 69 ++++++++-------- .../tests/BinaryFormatterEventSourceTests.cs | 2 +- .../tests/BinaryFormatterTests.cs | 2 +- .../tests/DisableBitTests.cs | 80 ++++++++++++------- .../tests/SerializationBinderTests.cs | 2 +- .../tests/SerializationGuardTests.cs | 2 +- ...time.Serialization.Formatters.Tests.csproj | 2 + .../tests/TestConfiguration.cs | 40 ++++++++++ .../ResourceManagerTests.cs | 2 +- 22 files changed, 260 insertions(+), 115 deletions(-) create mode 100644 src/libraries/System.Runtime.Serialization.Formatters/Directory.Build.targets rename src/libraries/System.Runtime.Serialization.Formatters/src/ILLink/{ILLink.Substitutions.NonMobile.xml => ILLink.Substitutions.xml} (100%) create mode 100644 src/libraries/System.Runtime.Serialization.Formatters/src/PACKAGE.md rename src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/{BinaryFormatter.PlatformNotSupported.cs => BinaryFormatter.Removed.cs} (88%) create mode 100644 src/libraries/System.Runtime.Serialization.Formatters/tests/TestConfiguration.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 0bc3f5f124210..abed01449f263 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -135,7 +135,9 @@ public static int SlowRuntimeTimeoutModifier public static bool IsThreadingSupported => (!IsWasi && !IsBrowser) || IsWasmThreadingSupported; public static bool IsWasmThreadingSupported => IsBrowser && IsEnvironmentVariableTrue("IsBrowserThreadingSupported"); public static bool IsNotWasmThreadingSupported => !IsWasmThreadingSupported; - public static bool IsBinaryFormatterSupported => IsNotMobile && !IsNativeAot; + + private static readonly Lazy s_isBinaryFormatterSupported = new Lazy(DetermineBinaryFormatterSupport); + public static bool IsBinaryFormatterSupported => s_isBinaryFormatterSupported.Value; public static bool IsStartingProcessesSupported => !IsiOS && !IstvOS; @@ -722,5 +724,34 @@ private static bool GetSupportsSha3() return false; } + + private static bool DetermineBinaryFormatterSupport() + { + if (IsNetFramework) + { + return true; + } + else if (IsNativeAot) + { + return false; + } + + Assembly assembly = typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).Assembly; + AssemblyName name = assembly.GetName(); + Version assemblyVersion = name.Version; + + bool isSupported = true; + + // Version 8.1 is the version in the shared runtime (.NET 9+) that has the type disabled with no config. + // Assembly versions beyond 8.1 are the fully functional version from NuGet. + // Assembly versions before 8.1 probably won't be encountered, since that's the past. + + if (assemblyVersion.Major == 8 && assemblyVersion.Minor == 1) + { + isSupported = false; + } + + return isSupported; + } } } diff --git a/src/libraries/System.Data.Common/tests/System/Data/DataSetTest.cs b/src/libraries/System.Data.Common/tests/System/Data/DataSetTest.cs index 0c9a4dc2a8fbc..523d4c47bf38f 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DataSetTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DataSetTest.cs @@ -1591,7 +1591,10 @@ public void SerializationFormat_Binary_does_not_work_by_default() #pragma warning restore SYSLIB0038 } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static bool RemoteExecutorBinaryFormatter => + RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported; + + [ConditionalFact(nameof(RemoteExecutorBinaryFormatter))] public void SerializationFormat_Binary_works_with_appconfig_switch() { RemoteExecutor.Invoke(RunTest).Dispose(); diff --git a/src/libraries/System.Data.Common/tests/System/Data/DataTableTest.cs b/src/libraries/System.Data.Common/tests/System/Data/DataTableTest.cs index 4fb880b51ea3c..3ef59e4973761 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DataTableTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DataTableTest.cs @@ -387,7 +387,10 @@ public void SerializationFormat_Binary_does_not_work_by_default() #pragma warning restore SYSLIB0038 } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static bool RemoteExecutorBinaryFormatter => + RemoteExecutor.IsSupported && PlatformDetection.IsBinaryFormatterSupported; + + [ConditionalFact(nameof(RemoteExecutorBinaryFormatter))] public void SerializationFormat_Binary_works_with_appconfig_switch() { RemoteExecutor.Invoke(RunTest).Dispose(); diff --git a/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/System.Resources.Extensions.BinaryFormat.Tests.csproj b/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/System.Resources.Extensions.BinaryFormat.Tests.csproj index 9eb577c43c89c..31756f1f73e7a 100644 --- a/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/System.Resources.Extensions.BinaryFormat.Tests.csproj +++ b/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/System.Resources.Extensions.BinaryFormat.Tests.csproj @@ -14,7 +14,9 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/Directory.Build.targets b/src/libraries/System.Runtime.Serialization.Formatters/Directory.Build.targets new file mode 100644 index 0000000000000..261ed184bc337 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Formatters/Directory.Build.targets @@ -0,0 +1,14 @@ + + + + + + 8.1.0.0 + + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/ref/System.Runtime.Serialization.Formatters.csproj b/src/libraries/System.Runtime.Serialization.Formatters/ref/System.Runtime.Serialization.Formatters.csproj index d6f7d98bc527d..40eeb1f34704b 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/ref/System.Runtime.Serialization.Formatters.csproj +++ b/src/libraries/System.Runtime.Serialization.Formatters/ref/System.Runtime.Serialization.Formatters.csproj @@ -1,12 +1,12 @@ - $(NetCoreAppCurrent) + $(NetCoreAppMinimum);$(NetCoreAppCurrent) - + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/ILLink/ILLink.Substitutions.NonMobile.xml b/src/libraries/System.Runtime.Serialization.Formatters/src/ILLink/ILLink.Substitutions.xml similarity index 100% rename from src/libraries/System.Runtime.Serialization.Formatters/src/ILLink/ILLink.Substitutions.NonMobile.xml rename to src/libraries/System.Runtime.Serialization.Formatters/src/ILLink/ILLink.Substitutions.xml diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/PACKAGE.md b/src/libraries/System.Runtime.Serialization.Formatters/src/PACKAGE.md new file mode 100644 index 0000000000000..63f69bffda56c --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/PACKAGE.md @@ -0,0 +1,17 @@ +## About + +Provides the legacy BinaryFormatter class for workloads which still require it. + +## Main Types + +The main types provided by this library are: + +* `System.Runtime.Serialization.Formatters.Binary.BinaryFormatter` + +## Additional Documentation + +* [Obsoletion Notice](https://aka.ms/binaryformatter) + +## Feedback & Contributing + +System.Runtime.Serialization.Formatters is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports are welcome at [the GitHub repository](https://github.com/dotnet/runtime). This package is considered legacy, and we only consider low-risk, high-impact fixes that are necessary to maintain functionality. diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/Resources/Strings.resx b/src/libraries/System.Runtime.Serialization.Formatters/src/Resources/Strings.resx index 47c6bf98bed08..b849fac89885c 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/Resources/Strings.resx @@ -252,4 +252,7 @@ BinaryFormatter serialization and deserialization are not supported on this platform. See https://aka.ms/binaryformatter for more information. + + BinaryFormatter serialization and deserialization have been removed. See https://aka.ms/binaryformatter for more information. + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj b/src/libraries/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj index dc685558d8c8a..7b5f4b4b3b4e0 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj @@ -1,20 +1,25 @@ - $(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-android + $(NetCoreAppCurrent);$(NetCoreAppMinimum) false + true + + false - $(TargetFramework.SubString($([MSBuild]::Add($(TargetFramework.IndexOf('-')), 1)))) - - $(NoWarn);CS0649 $(NoWarn);CA1822;IDE0060 + + true + + false + true - - + + @@ -37,17 +42,35 @@ + + + + + + - + + + + + + + + + + + @@ -55,37 +78,19 @@ - - - - - - - - - - - - - - - - - + Common\System\LocalAppContextSwitches.Common.cs - - + + - + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Core.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Core.cs index d3d9aefe515d7..cc8fdaff3328a 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Core.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Core.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -8,6 +9,10 @@ namespace System.Runtime.Serialization.Formatters.Binary { public sealed partial class BinaryFormatter : IFormatter { + private static readonly ConcurrentDictionary s_typeNameCache = new ConcurrentDictionary(); + + internal object[]? _crossAppDomainArray; + [RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)] public object Deserialize(Stream serializationStream) @@ -88,5 +93,12 @@ public void Serialize(Stream serializationStream, object graph) BinaryFormatterEventSource.Log.SerializationStop(); } } + + internal static TypeInformation GetTypeInformation(Type type) => + s_typeNameCache.GetOrAdd(type, t => + { + string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom); + return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom); + }); } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.PlatformNotSupported.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Removed.cs similarity index 88% rename from src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.PlatformNotSupported.cs rename to src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Removed.cs index cf124a5c6de1c..1c9427e02833b 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.PlatformNotSupported.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.Removed.cs @@ -11,10 +11,10 @@ public sealed partial class BinaryFormatter : IFormatter [RequiresDynamicCode(IFormatter.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)] public object Deserialize(Stream serializationStream) - => throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationNotSupportedOnThisPlatform); + => throw new PlatformNotSupportedException(SR.BinaryFormatter_Removed); [RequiresUnreferencedCode(IFormatter.RequiresUnreferencedCodeMessage)] public void Serialize(Stream serializationStream, object graph) - => throw new PlatformNotSupportedException(SR.BinaryFormatter_SerializationNotSupportedOnThisPlatform); + => throw new PlatformNotSupportedException(SR.BinaryFormatter_Removed); } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs index 11b9fb735dfdd..591145b659a76 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs @@ -1,22 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Concurrent; - namespace System.Runtime.Serialization.Formatters.Binary { [Obsolete(Obsoletions.BinaryFormatterMessage, DiagnosticId = Obsoletions.BinaryFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public sealed partial class BinaryFormatter : IFormatter { - private static readonly ConcurrentDictionary s_typeNameCache = new ConcurrentDictionary(); - internal ISurrogateSelector? _surrogates; internal StreamingContext _context; internal SerializationBinder? _binder; internal FormatterTypeStyle _typeFormat = FormatterTypeStyle.TypesAlways; // For version resiliency, always put out types internal FormatterAssemblyStyle _assemblyFormat = FormatterAssemblyStyle.Simple; internal TypeFilterLevel _securityLevel = TypeFilterLevel.Full; - internal object[]? _crossAppDomainArray; public FormatterTypeStyle TypeFormat { get { return _typeFormat; } set { _typeFormat = value; } } public FormatterAssemblyStyle AssemblyFormat { get { return _assemblyFormat; } set { _assemblyFormat = value; } } @@ -34,12 +29,5 @@ public BinaryFormatter(ISurrogateSelector? selector, StreamingContext context) _surrogates = selector; _context = context; } - - internal static TypeInformation GetTypeInformation(Type type) => - s_typeNameCache.GetOrAdd(type, t => - { - string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom); - return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom); - }); } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryObjectReader.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryObjectReader.cs index 84ab775450482..9f8f1a389409c 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryObjectReader.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryObjectReader.cs @@ -87,48 +87,49 @@ internal object Deserialize(BinaryParser serParser) _isSimpleAssembly = (_formatterEnums._assemblyFormat == FormatterAssemblyStyle.Simple); - using (DeserializationToken token = SerializationInfo.StartDeserialization()) - { - if (_fullDeserialization) - { - // Reinitialize - _objectManager = new ObjectManager(_surrogates, _context); - _serObjectInfoInit = new SerObjectInfoInit(); - } + // When BinaryFormatter was built-in to the platform we used to activate SerializationGuard here, + // but now that it has moved to an OOB offering it no longer does. - // Will call back to ParseObject, ParseHeader for each object found - serParser.Run(); + if (_fullDeserialization) + { + // Reinitialize + _objectManager = new ObjectManager(_surrogates, _context); + _serObjectInfoInit = new SerObjectInfoInit(); + } - if (_fullDeserialization) - { - _objectManager!.DoFixups(); - } + // Will call back to ParseObject, ParseHeader for each object found + serParser.Run(); - if (TopObject == null) - { - throw new SerializationException(SR.Serialization_TopObject); - } + if (_fullDeserialization) + { + _objectManager!.DoFixups(); + } - //if TopObject has a surrogate then the actual object may be changed during special fixup - //So refresh it using topID. - if (HasSurrogate(TopObject.GetType()) && _topId != 0)//Not yet resolved - { - Debug.Assert(_objectManager != null); - TopObject = _objectManager.GetObject(_topId); - } + if (TopObject == null) + { + throw new SerializationException(SR.Serialization_TopObject); + } - if (TopObject is IObjectReference) - { - TopObject = ((IObjectReference)TopObject).GetRealObject(_context); - } + //if TopObject has a surrogate then the actual object may be changed during special fixup + //So refresh it using topID. + if (HasSurrogate(TopObject.GetType()) && _topId != 0) //Not yet resolved + { + Debug.Assert(_objectManager != null); + TopObject = _objectManager.GetObject(_topId); + } - if (_fullDeserialization) - { - _objectManager!.RaiseDeserializationEvent(); // This will raise both IDeserialization and [OnDeserialized] events - } + if (TopObject is IObjectReference) + { + TopObject = ((IObjectReference)TopObject).GetRealObject(_context); + } - return TopObject!; + if (_fullDeserialization) + { + _objectManager!. + RaiseDeserializationEvent(); // This will raise both IDeserialization and [OnDeserialized] events } + + return TopObject!; } private bool HasSurrogate(Type t) { diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs index 805b1ad4df481..7756ba5b2f5e7 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs @@ -11,7 +11,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ConditionalClass(typeof(TestConfiguration), nameof(TestConfiguration.IsBinaryFormatterEnabled))] public static class BinaryFormatterEventSourceTests { private const string BinaryFormatterEventSourceName = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource"; diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 95891b7bcfa3a..82e7f045e384d 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -18,7 +18,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ConditionalClass(typeof(TestConfiguration), nameof(TestConfiguration.IsBinaryFormatterEnabled))] public partial class BinaryFormatterTests : FileCleanupTestBase { // On 32-bit we can't test these high inputs as they cause OutOfMemoryExceptions. diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/DisableBitTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/DisableBitTests.cs index d74a5089119ce..d42e51679ce80 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/DisableBitTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/DisableBitTests.cs @@ -14,9 +14,10 @@ public static class DisableBitTests 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; + public static bool IsBinaryFormatterSuppressedOnThisPlatform => !TestConfiguration.IsBinaryFormatterSupported; + + public static bool IsFeatureSwitchIgnored = !TestConfiguration.IsFeatureSwitchRespected; - private const string EnableBinaryFormatterSwitchName = "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization"; private const string MoreInfoUrl = "https://aka.ms/binaryformatter"; [ConditionalFact(nameof(IsBinaryFormatterSuppressedOnThisPlatform))] @@ -39,43 +40,66 @@ public static void DisabledAlwaysInBrowser() public static void EnabledThroughFeatureSwitch() { RemoteInvokeOptions options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions[EnableBinaryFormatterSwitchName] = bool.TrueString; - - RemoteExecutor.Invoke(() => - { - // Test serialization + options.RuntimeConfigurationOptions[TestConfiguration.EnableBinaryFormatterSwitchName] = bool.TrueString; - MemoryStream ms = new MemoryStream(); - new BinaryFormatter().Serialize(ms, "A string to serialize."); - - // Test round-trippability - - ms.Position = 0; - object roundTripped = new BinaryFormatter().Deserialize(ms); - Assert.Equal("A string to serialize.", roundTripped); - }, options).Dispose(); + RunRemoteTest(options, TestConfiguration.IsBinaryFormatterSupported); } [ConditionalFact(nameof(ShouldRunFullFeatureSwitchEnablementChecks))] public static void DisabledThroughFeatureSwitch() { RemoteInvokeOptions options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions[EnableBinaryFormatterSwitchName] = bool.FalseString; + options.RuntimeConfigurationOptions[TestConfiguration.EnableBinaryFormatterSwitchName] = bool.FalseString; - RemoteExecutor.Invoke(() => - { - // First, test serialization + bool expectSuccess = TestConfiguration.IsBinaryFormatterSupported; - MemoryStream ms = new MemoryStream(); - BinaryFormatter bf = new BinaryFormatter(); - var ex = Assert.Throws(() => bf.Serialize(ms, "A string to serialize.")); - Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL + if (TestConfiguration.IsFeatureSwitchRespected) + { + expectSuccess = false; + } - // Then test deserialization + RunRemoteTest(options, expectSuccess); + } - ex = Assert.Throws(() => bf.Deserialize(ms)); - Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL - }, options).Dispose(); + private static void RunRemoteTest(RemoteInvokeOptions options, bool expectSuccess) + { + if (expectSuccess) + { + RemoteExecutor.Invoke( + () => + { + // Test serialization + + MemoryStream ms = new MemoryStream(); + new BinaryFormatter().Serialize(ms, "A string to serialize."); + + // Test round-trippability + + ms.Position = 0; + object roundTripped = new BinaryFormatter().Deserialize(ms); + Assert.Equal("A string to serialize.", roundTripped); + }, + options).Dispose(); + } + else + { + RemoteExecutor.Invoke( + () => + { + // First, test serialization + + MemoryStream ms = new MemoryStream(); + BinaryFormatter bf = new BinaryFormatter(); + var ex = Assert.ThrowsAny(() => 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.ThrowsAny(() => bf.Deserialize(ms)); + Assert.Contains(MoreInfoUrl, ex.Message, StringComparison.Ordinal); // error message should link to the more info URL + }, + options).Dispose(); + } } } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationBinderTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationBinderTests.cs index daf11b1f68678..addfb852635a7 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationBinderTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationBinderTests.cs @@ -21,7 +21,7 @@ public void BindToName_NullDefaults() Assert.Null(typeName); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ConditionalFact(typeof(TestConfiguration), nameof(TestConfiguration.IsBinaryFormatterEnabled))] public void BindToType_AllValuesTracked() { var s = new MemoryStream(); diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationGuardTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationGuardTests.cs index 7c294ff0a6c1c..3805070b47d8e 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationGuardTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationGuardTests.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ConditionalClass(typeof(TestConfiguration), nameof(TestConfiguration.IsBinaryFormatterEnabled))] public static class SerializationGuardTests { [Fact] diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj b/src/libraries/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj index a521f40739da6..03df94a257abd 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj @@ -20,6 +20,7 @@ + @@ -47,6 +48,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/TestConfiguration.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/TestConfiguration.cs new file mode 100644 index 0000000000000..505a56ff096d8 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/TestConfiguration.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Serialization.Formatters.Tests +{ + public static class TestConfiguration + { + internal const string EnableBinaryFormatterSwitchName = "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization"; + + public static readonly bool IsBinaryFormatterSupported; + public static readonly bool IsBinaryFormatterEnabled; + public static readonly bool IsFeatureSwitchRespected; + + static TestConfiguration() + { + if (!PlatformDetection.IsBinaryFormatterSupported) + { + return; + } + + IsBinaryFormatterSupported = true; + IsBinaryFormatterEnabled = true; + + if (PlatformDetection.IsNetFramework) + { + Console.WriteLine("BinaryFormatter is always enabled by the platform (netfx)."); + return; + } + + IsFeatureSwitchRespected = true; + + if (!AppContext.TryGetSwitch(EnableBinaryFormatterSwitchName, out IsBinaryFormatterEnabled)) + { + IsBinaryFormatterEnabled = false; + } + + Console.WriteLine($"BinaryFormatter is enabled in AppConfig: {IsBinaryFormatterEnabled}"); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/ResourceManagerTests.cs b/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/ResourceManagerTests.cs index b80b709fd171f..82e55c43111df 100644 --- a/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/ResourceManagerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/ResourceManagerTests.cs @@ -392,7 +392,7 @@ public static void GetResourceSet_NonStringsIgnoreCase(string key, object expect Assert.Equal(expectedValue, set.GetObject(key.ToLower(), true)); } - public static bool IsDrawingSupportedAndAllowsCustomResourceTypes => PlatformDetection.IsDrawingSupported && AllowsCustomResourceTypes; + public static bool IsDrawingSupportedAndAllowsCustomResourceTypes => PlatformDetection.IsDrawingSupported && AllowsCustomResourceTypes && PlatformDetection.IsBinaryFormatterSupported; [ConditionalTheory(nameof(IsDrawingSupportedAndAllowsCustomResourceTypes))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34008", TestPlatforms.Linux | TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]