-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Remove BinaryFormatter usage from System.ComponentModel.TypeConverter #39293
Comments
Tagging subscribers to this area: @safern |
I looked at the public static void Serialize(Stream o, string cryptoKey, DesigntimeLicenseContext context)
{
if (AppContext.TryGetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", out bool isEnabled) && isEnabled)
{
IFormatter formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011 // Issue https://github.com/dotnet/runtime/issues/39293 tracks finding an alternative to BinaryFormatter
formatter.Serialize(o, new object[] { cryptoKey, context._savedLicenseKeys });
#pragma warning restore SYSLIB0011
}
else
{
using (BinaryWriter writer = new BinaryWriter(o, encoding: Text.Encoding.UTF8, leaveOpen: true))
{
writer.Write(cryptoKey);
string licenseKeys = System.Text.Json.JsonSerializer.Serialize(context._savedLicenseKeys);
writer.Write(licenseKeys);
}
}
}
internal static void Deserialize(Stream o, string cryptoKey, RuntimeLicenseContext context)
{
if (AppContext.TryGetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", out bool isEnabled) && isEnabled)
{
#pragma warning disable SYSLIB0011 // Issue https://github.com/dotnet/runtime/issues/39293 tracks finding an alternative to BinaryFormatter
IFormatter formatter = new BinaryFormatter();
object obj = formatter.Deserialize(o);
#pragma warning restore SYSLIB0011
if (obj is object[] value)
{
if (value[0] is string && (string)value[0] == cryptoKey)
{
context._savedLicenseKeys = (Hashtable)value[1];
}
}
}
else
{
using (BinaryReader reader = new BinaryReader(o, encoding: Text.Encoding.UTF8, leaveOpen: true))
{
string streamCryptoKey = reader.ReadString();
if (streamCryptoKey == cryptoKey)
{
string licenseKeys = reader.ReadString();
context._savedLicenseKeys = System.Text.Json.JsonSerializer.Deserialize<Hashtable>(licenseKeys);
}
}
} The only wrinkle I can think of here is if the output Also, to test the |
@ericstj what are the guidelines for |
Extensions tests use it for legacy reasons. In general we try to avoid testing using internals. Are you sure that changing the format here is even legitimate? It looks to me like the entire scenario for this type is to consume external data, which is presumably only of that specific format. If you want to change the format you need to somehow record that in the data itself. One way of doing this is with some sort of header on the data that can inform the consuming code which implementation to use. Here you might be able to use a sentinel data that is unlikely/impossible to show up in the binaryFormatted data, use that as the header and probe for it. I see:
HashTable is a loosely typed collection. It looks to me like this might be string/string, but you should double check that. I think there are better ways to serialize this /cc @layomia Finally, who calls serialize? I seem to remember it's a commandline tool that might only exist on desktop. Will customers have a way to use this technology and leverage your change of format? |
Why though? IIUC, customers use Binary Formatter right now for both
Yup, I did double check that the
(Caveat that I don't understand this library very well). No idea. AFAICT, there are no calls to
|
I agree with Eric's comment that there's no need to use System.Text.Json for this scenario. Looking through the code, the hash set is a collection of string keys with string values. So this could be done as: public void Serialize(string cryptoKey, Hashset data, BinaryWriter writer)
{
writer.Write(cryptoKey);
writer.Write(data.Count);
foreach (var kvp in data)
{
writer.Write(kvp.Key);
writer.Write(kvp.Value);
}
}
public void Deserialize(BinaryReader reader, string cryptoKey, Hashset data)
{
if (reader.ReadString() != cryptoKey) { /* FAIL */ }
int numEntries = reader.ReadInt32();
while (int i = 0; i < numEntries; i++)
{
data.Add(reader.ReadString(), reader.ReadString();
}
}
The EnableUnsafeBinaryFormatterSerialization flag should only control whether
Careful with this approach. We mustn't end up in a situation where the payload itself can say "I'm BinaryFormatter-formatted!" and activate the insecure code paths without the app developer having explicitly stated "I consent to allowing LicenseManager to do this." |
Ah indeed, we can serialize the individual values in the HashTable directly. I was only putting up a quick first draft.
Interesting. I didn't consider that, but it makes sense. Would defining a flag like this work? (copied from S.P.CoreLib) internal static partial class LocalAppContextSwitches
{
private static int s_enableUnsafeUTF7Encoding; // rename to something like enableBinaryFormatterUse
public static bool EnableUnsafeUTF7Encoding
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue("System.Text.Encoding.EnableUnsafeUTF7Encoding", ref s_enableUnsafeUTF7Encoding);
}
} |
Of course the developer would have to consent to this same as any other BF usage. I’m pointing out that you are really only changing one side of a data format without thinking about updating whatever writer is out their or helping people with migration.
Have you understood the end to end use case for this type? Where do people get the files they are feeding into it? Perhaps you can look at some of the usage in desktop to get a better idea. Cc @OliaG |
If there's a scenario where core writes the data file and netfx reads it, then absolutely we should consider porting to netfx whatever changes we make here. Regardless, it's fine for the payload to say "I'm using <xyz> serialization technology" (there are well-known markers for BinaryFormatter), and for the consumer to switch on that marker, as long as the consumer has opted in to understanding it. The point of these exercises is that the BinaryFormatter-related code paths remain completely deactivated unless the consumer has opted in to reactivating them. Here, the "consumer" is the app developer. The payload itself may be untrusted and must not be the component in charge of this decision. |
Right if the payload said it needed BinaryFormatter and the app didn’t allow it that should be an exception. That’s the whole premise of this work. |
Just to drop some pointers, the caller to this Serialize method is The tool is not supported on .NETCore: The current workaround is for folks to build the lcix file on .NETframework and use it on core, as mentioned in that issue. There is MSBuild integration: And it is passed to the compiler and embedded into the assembly: So, we have a compatibility issue here consuming binaries which embedded the binary-formatted format. We also don't have a path forward since we aren't building a new version of this tool on .NETCore in order to produce the new format. |
Just to be clear, the UX in my mind here was : Call
Ah I see, nice find. That begs the question, is deprecating BF considered a big enough ask to make a change in .NET Framework code? If we assume that .NETFramework remains untouched, we only need to consider the case where .NET Core tries to consume a resource from Framework that is binary-formatted. If The real concern is what we want to do when BF support is completely dropped for .NET 7? It would be simple to write a console app/tool that converts a Binary-Formatted resource into something |
No, that was only if we decided to change the format that's written. If we did that we'd need to be able to see if the payload was BF or something else.
Not likely, we aren't deprecating there.
Yeah, this thing is effectively broken unless you turn on that flag. That's probably OK for now.
I don't think we'll touch existing binaries. Those will need the flag. Also, I've heard folks say that we will pull BF but may have a package that folks can use to add it back if they choose to. This might just be a rumor ;)
This is sort of like the path we are on for resources. I think the complete solution here would be to bring back the code for the LC task to .NETCore: porting it to use a different serialization technology (eg: maybe just write those key-value pairs). This is similar to the work we did in 5.0 for resources in MSBuild. The nice thing about this technology is that it seems the file checked into the user's source is not a BinaryFormatted payload. According to the docs it is an |
Ref:
runtime/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs
Lines 26 to 45 in af828ae
This issue tracks the removal and replacement of this code per the
BinaryFormatter
obsoletion plan.The text was updated successfully, but these errors were encountered: