Skip to content
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

Introduce opt-in BinaryFormatter killbit #38963

Merged
merged 20 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ private static bool GetSwitchDefaultValue(string switchName)
return true;
}

if (switchName == "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")
{
return true;
}

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public abstract partial class IEnumerable_Generic_Tests<T> : TestBase<T>
{
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
[PlatformSpecific(~TestPlatforms.Browser)] // BinaryFormatter not supported in browser
GrabYourPitchforks marked this conversation as resolved.
Show resolved Hide resolved
public void IGenericSharedAPI_SerializeDeserialize(int count)
{
IEnumerable<T> expected = GenericIEnumerableFactory(count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public abstract partial class IEnumerable_NonGeneric_Tests : TestBase
{
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
[PlatformSpecific(~TestPlatforms.Browser)] // BinaryFormatter not supported in browser
public void IGenericSharedAPI_SerializeDeserialize(int count)
{
IEnumerable expected = NonGenericIEnumerableFactory(count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace System.Collections.Generic.Tests
public abstract partial class ComparersGenericTests<T>
{
[Fact]
[PlatformSpecific(~TestPlatforms.Browser)] // BinaryFormatter not supported in browser
public void EqualityComparer_SerializationRoundtrip()
{
var bf = new BinaryFormatter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ private static IDictionary<T, T> CreateDictionary<T>(int size, Func<int, T> keyV
}

[Fact]
[PlatformSpecific(~TestPlatforms.Browser)] // BinaryFormatter not supported in browser
public void ComparerSerialization()
{
// Strings switch between randomized and non-randomized comparers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ public void Remove_NonDefaultComparer_ComparerUsed(int capacity)
#region Serialization

[Fact]
[PlatformSpecific(~TestPlatforms.Browser)] // BinaryFormatter not supported in browser
public void ComparerSerialization()
{
// Strings switch between randomized and non-randomized comparers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
<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.Runtime.Serialization.SerializationInfo">
<method signature="System.Boolean get_BinaryFormatterEnabled()" body="stub" value="false" feature="System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization" featurevalue="false" />
</type>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,7 @@
<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" />
Expand All @@ -830,6 +831,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\VersioningHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\RuntimeType.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\SByte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SecureAppContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\AllowPartiallyTrustedCallersAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\CryptographicException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\IPermission.cs" />
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/AppContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ internal static unsafe void Setup(char** pNames, char** pValues, int count)
{
s_dataStore.Add(new string(pNames[i]), new string(pValues[i]));
}

SecureAppContext.Initialize();
}

private static string GetBaseDirectoryCore()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public static bool PreserveEventListnerObjectIdentity
get => GetCachedSwitchValue("Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity", ref s_preserveEventListnerObjectIdentity);
}

private static int s_binaryFormatterEnabled;
public static bool BinaryFormatterEnabled
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", ref s_binaryFormatterEnabled);
}

private static int s_serializationGuard;
public static bool SerializationGuard
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;

Expand Down Expand Up @@ -37,7 +38,7 @@ internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCa

_ums = stream as UnmanagedMemoryStream;

_permitDeserialization = permitDeserialization;
_permitDeserialization = permitDeserialization && SerializationInfo.BinaryFormatterEnabled;

ReadResources();
}
Expand Down Expand Up @@ -67,6 +68,12 @@ private object DeserializeObject(int typeIndex)

private void InitializeBinaryFormatter()
{
if (!SerializationInfo.BinaryFormatterEnabled)
{
// allows the linker to trim away all the reflection goop below
throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
}

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)!);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// 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>();

public static bool BinaryFormatterEnabled
{
get
{
// The "safe" value for this switch is DISABLED.
// This means that we want the feature to be disabled if it
// was disabled at app start *or* somebody later disabled it
// via a manual call to AppContext.SetSwitch.
//
// De Morgan's theorem:
// final_disabled = disabled_A || disabled_B
// => final_disabled = !(!disabled_A && !disabled_B)
// => !final_disabled = !disabled_A && !disabled_B
// => final_enabled = enabled_A && enabled_B

return SecureAppContext.BinaryFormatterEnabled
&& LocalAppContextSwitches.BinaryFormatterEnabled;
}
}

public static bool SerializationGuardEnabled
{
get
{
// The "safe" value for this switch is ENABLED.
// This means that we want the feature to be enabled if it
// was enabled at app start *or* somebody later enabled it
// via a manual call to AppContext.SetSwitch.

return SecureAppContext.SerializationGuardEnabled
|| LocalAppContextSwitches.SerializationGuard;
}
}

#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 (SerializationGuardEnabled)
{
#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);
}
}
}
Loading